智慧社区

news/2025/3/10 16:20:53/文章来源:https://www.cnblogs.com/ssrheart/p/18237217

智慧社区

# 智慧社区-小程序-欢迎页面-首页-轮播图-公告-信息采集,社区活动,人脸检测,语音识别,心率检测,积分商城-信息采集页面-采集人数-采集详情页面-采集统计页面-人脸检测页面-语音识别页面-积分商城页面-活动-活动列表-报名活动-加载更多-公告-公告列表-我的-信息展示-登录

欢迎页面

后端

models

class Welcome(models.Model):img = models.ImageField(upload_to='welcome', default='slash.png')order = models.IntegerField()link = models.CharField(max_length=32)create_time = models.DateTimeField(auto_now=True)is_delete = models.BooleanField(default=False)

views

from django.shortcuts import render
from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import ListModelMixin
from .serializers import WelcomeSerializer
from .models import Welcomeclass WelcomeView(GenericViewSet, ListModelMixin):queryset = Welcome.objects.all().filter(is_delete=False).order_by("-order")serializer_class = WelcomeSerializer

serializer

class WelcomeSerialzier(serializers.ModelSerializer):class Meta:model=Welcomefields=['img','link']

前端

# 1 欢迎页面-onLoad---》向后端发送请求-拿回图片,盖住整个屏幕-倒计时3s(跳过)--》进入到首页

wxml

<view class="container"><text bindtap="doJump" class="jump">跳过{{seconds}}秒</text><image class="img" src="{{img}}" bind:tap="goPage"/>
</view>

wxss

page{height: 100%;
}.container {height: 100%;width: 100%;
} .container .img{height: 100%;width: 100%;
}.jump{font-size: 30rpx;position: absolute;left: 600rpx;top: 80rpx;background-color: #dddddd;padding: 10rpx 20rpx;border-radius: 20rpx;
}

js

import settings from '../../static/js/settings.js'
Page({data: {seconds:3,img:'/static/img/bg/splash3.png',url:'/pages/log/log'},onLoad(options) {// 发送请求,获取欢迎页wx.request({url: settings.welcome,method:'GET',success:(res)=>{this.setData({img:res.data[0].img,url:res.data[0].link})}})let t=setInterval(()=>{if(this.data.seconds <= 0){//定时器清除clearInterval(t)//跳转到项目页面 + 配置tabBarwx.switchTab({url: '/pages/index/index',})}else{this.setData({seconds:this.data.seconds - 1})}},1000)},goPage(){wx.navigateTo({url: '/pages/second/collection/collection',})},doJump(){wx.switchTab({url: '/pages/index/index',})}
})

image-20240528185350249

首页

"pages": ["pages/welcome/welcome","pages/index/index","pages/my/my","pages/activity/activity","pages/notice/notice",
],

前端

wxml

<view class="container"><!-- 轮播图 --><view class="banner"><swiper autoplay indicator-dots circular indicator-color='#FFFFF' interval='3000'><swiper-item><image src="/static/img/banner/banner1.png" mode="widthFix" /></swiper-item><swiper-item><image src="/static/img/banner/banner2.png" mode="widthFix" /></swiper-item><swiper-item><image src="/static/img/banner/banner3.png" mode="widthFix" /></swiper-item></swiper></view><van-notice-bar left-icon="volume-o" text="在代码阅读过程中人们说脏话的频率是衡量代码质量的唯一标准。" /><van-grid column-num="3"><van-grid-item icon="/static/img/menu/ht.png" text="信息采集" bind:click="gotoCollection" /><van-grid-item icon="/static/img/menu/wyf.png" text="社区活动" bind:click="gotoActivity" /><van-grid-item icon="/static/img/menu/wygl.png" text="人脸检测" bind:click="gotoFace" /><!-- 可以根据需要添加更多的快捷入口 --><van-grid-item icon="/static/img/menu/wylx.png" text="语音识别" bind:click="gotoVoice" /><van-grid-item icon="https://b.yzcdn.cn/vant/icon-demo-1126.png" text="心率检测" bind:click="gotoHeart" /><van-grid-item icon="/static/img/menu/ht.png" text="积分商城" bind:click="gotoGoods" /></van-grid><view class="bottom"><view><image src="/static/img/home/cute_1.jpg" mode="scaleToFill" /></view><view><image src="/static/img/home/cute_2.jpg" mode="scaleToFill" /></view><view><image src="/static/img/home/cute_3.jpg" mode="scaleToFill" /></view><view><image src="/static/img/home/cute_4.jpg" mode="scaleToFill" /></view></view>
</view>

wxss

.banner image{width: 100%; /* 图片宽度拉伸至容器宽度 */height: 100%; /* 图片高度拉伸至容器高度 */object-fit: cover; /* 图片将覆盖整个容器区域,可能被裁剪 */
}.bottom{display: flex;justify-content: space-evenly;margin-top: 20rpx;flex-wrap: wrap;
}.bottom>view>image{width: 345rpx;height: 200rpx;
}

js

// index.js
Page({gotoCollection(){wx.navigateTo({url: '/pages/second/collection/collection'})},gotoFace(){wx.navigateTo({url: '/pages/second/face/face'})},gotoVoice(){wx.navigateTo({url: '/pages/second/voice/voice'})},gotoActivity(){wx.switchTab({url: '/pages/activity/activity',})},gotoHeart(){wx.navigateTo({url: '/pages/second/heart/heart',})},gotoGoods(){wx.navigateTo({url: '/pages/second/goods/goods',})},
})

image-20240528185421570

信息采集

<view class="container"><view class="top"><view class="tip">今日采集数量(人)</view><view class="count">{{dataDict.today_count}}</view></view><view class="function"><view class="menu" style="border-right:1rpx solid #ddd;" bindtap="bindToForm"> <text class="iconfont icon-xinxicaiji"></text> 信息采集</view><view class="menu" bindtap="bindToStatistics">  <text class="iconfont icon-shujutongji" ></text> 数据统计</view></view><view class="table"><view class="item"><view class="title">社区信息列表({{dataDict.today_count}}人)</view></view><view class="item" wx:for="{{dataDict.result}}" wx:for-item="row" wx:key="index"><view class="record"><view class="avatar"><image src="{{row.avatar}}"></image></view><view class="desc"><view class="username">{{row.name}}</view><view><view class="txt-group"><label class="zh">网格区域</label><label class="en"> | {{row.area.desc}}</label></view><view class="area"> <label class="fa fa-map-marker"></label> {{row.area.name}} </view></view></view><view class="delete" bindtap="doDeleteRow" data-nid="{{row.id}}" data-index="{{index}}" ><label class="iconfont icon-shanchu"></label></view></view></view></view>
</view>

wxss

.top {background-color: #01ccb6;height: 200rpx;display: flex;flex-direction: column;align-items: center;color: white;
}.top .tip {font-size: 22rpx;font-weight: 100;
}.top .count {padding: 10rpx;font-size: 58rpx;
}.function {display: flex;flex-direction: row;justify-content: space-around;background-color: #02bfae;
}.function .menu {font-size: 28rpx;margin: 25rpx 0;color: white;width: 50%;text-align: center;flex-direction: row;flex-direction: column;align-items: center;
}.table .item {border-bottom: 1rpx solid #e7e7e7;}.table .item .title{margin: 20rpx 30rpx;padding-left: 10rpx;border-left: 5rpx solid #02bfae;font-size: 26rpx;
}.record{margin: 30rpx 40rpx;display: flex;flex-direction: row;justify-content: flex-start;
}.record .avatar{width: 200rpx;height: 200rpx;
}.record .avatar image{width: 100%;height: 100%;border-radius: 30rpx;
}.record .desc{margin: 0 40rpx;
}
.desc{width: 290rpx;display: flex;flex-direction: column;justify-content: space-between;
}
.desc .username{margin-top: 25rpx;font-size: 38rpx;
}.txt-group{font-size: 27rpx;margin: 10rpx 0;
}
.txt-group .zh{color: #8c8c8c;
}.txt-group .en{color: #cccccc;
}.area{color: #00c8b6;font-weight: bold;
}.delete{width: 100rpx;color: red;text-align: center;display: flex;flex-direction: column;justify-content: center;
}

json

{"usingComponents": {},"navigationBarBackgroundColor": "#01ccb6","navigationBarTitleText": "","enablePullDownRefresh": true,"navigationBarTextStyle":"white","backgroundColor":"#01ccb6"
}

js

// pages/second/collection/collection.js
Page({data: {},onLoad(options) {},bindToForm(){wx.navigateTo({url: '/pages/second/camera/camera',})}
})

image-20240528185732014

camera

wxml

<camera class="camera"  device-position="{{ backFront ? 'back' : 'front' }}"  flash="off" frame-size="medium" ></camera><view class="function"><view class="switch"> </view><view class="record" bindtap="takePhoto"><image src="/static/img/camera/record_on.png"></image></view><view class="switch" bindtap="switchCamera"><image src="/static/img/camera/rotate-camera-white.png"></image></view></view>
page{height: 100%;
}
.camera{height: 80%;width: 100%;
}.function{height: 20%;background-color: black;display: flex;flex-direction: row;justify-content: space-around;align-items: center;
}.record image{width: 160rpx;height: 160rpx;
}.switch{color: white;width: 80rpx;height: 80rpx;
}.switch image{width: 80rpx;height: 80rpx;
}
Page({data: {backFront:true},switchCamera(e) {var old = this.data.backFrontthis.setData({backFront: !old})},})

image-20240528185743783

首页轮播图和公告接口

前端

html

<view class="container"><!-- 轮播图 --><view class="banner"><swiper autoplay indicator-dots circular indicator-color='#FFFFF' interval='3000'><swiper-item wx:for="{{banner_list}}" wx:key="id"><image src="{{item.img}}" mode="widthFix" /></swiper-item></swiper></view><van-notice-bar left-icon="volume-o" text="{{notice}}" /><van-grid column-num="3"><van-grid-item icon="/static/img/menu/ht.png" text="信息采集" bind:click="gotoCollection" /><van-grid-item icon="/static/img/menu/wyf.png" text="社区活动" bind:click="gotoActivity" /><van-grid-item icon="/static/img/menu/wygl.png" text="人脸检测" bind:click="gotoFace" /><!-- 可以根据需要添加更多的快捷入口 --><van-grid-item icon="/static/img/menu/wylx.png" text="语音识别" bind:click="gotoVoice" /><van-grid-item icon="https://b.yzcdn.cn/vant/icon-demo-1126.png" text="心率检测" bind:click="gotoHeart" /><van-grid-item icon="/static/img/menu/ht.png" text="积分商城" bind:click="gotoGoods" /></van-grid><view class="bottom"><view><image src="/static/img/home/cute_1.jpg" mode="scaleToFill" /></view><view><image src="/static/img/home/cute_2.jpg" mode="scaleToFill" /></view><view><image src="/static/img/home/cute_3.jpg" mode="scaleToFill" /></view><view><image src="/static/img/home/cute_4.jpg" mode="scaleToFill" /></view></view></view>

css

.banner image{width: 100%; /* 图片宽度拉伸至容器宽度 */height: 100%; /* 图片高度拉伸至容器高度 */object-fit: cover; /* 图片将覆盖整个容器区域,可能被裁剪 */
}
.bottom{display: flex;justify-content: space-evenly;margin-top: 20rpx;flex-wrap: wrap;
}.bottom>view>image{width: 345rpx;height: 200rpx;
}

js

import settings from '../../static/js/settings.js'
Page({data: {banner_list: {},notice: ''},onLoad(options) {wx.request({url: settings.banner,method:'GET',success:(res)=>{if(res.data.code==100){this.setData({banner_list:res.data.banner,notice:res.data.notice.title + "快来玩呀~~~~嗯~~~"})}else{wx.showToast({title: '轮播图网络加载失败',})}}})},gotoCollection() {wx.navigateTo({url: '/pages/second/collection/collection'})},gotoFace() {wx.navigateTo({url: '/pages/second/face/face'})},gotoVoice() {wx.navigateTo({url: '/pages/second/voice/voice'})},gotoActivity() {wx.switchTab({url: '/pages/activity/activity',})},gotoHeart() {wx.navigateTo({url: '/pages/second/heart/heart',})},gotoGoods() {wx.navigateTo({url: '/pages/second/goods/goods',})},
})

image-20240529150327551

后端

views

from django.shortcuts import render
from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import ListModelMixin
from .serializers import WelcomeSerializer, BannerSerializer, NoticeSerializer
from .models import Welcome, Notice, Banner
from rest_framework.response import Response# 广告
class WelcomeView(GenericViewSet, ListModelMixin):queryset = Welcome.objects.all().filter(is_delete=False).order_by("-order")serializer_class = WelcomeSerializer# 轮播图
class BannerView(GenericViewSet, ListModelMixin):queryset = Banner.objects.all().filter(is_delete=False).order_by("order")[:2]serializer_class = BannerSerializerdef list(self, request, *args, **kwargs):res = super().list(request, *args, **kwargs)notice = Notice.objects.all().order_by("create_time").first()serializer = NoticeSerializer(instance=notice)return Response({"code": 100, "msg": "成功", "banner": res.data, "notice": serializer.data})

models

from django.db import models# 广告表
class Welcome(models.Model):img = models.ImageField(upload_to="welcome", default="slash.png")order = models.IntegerField()link = models.CharField(max_length=32)create_time = models.DateTimeField(auto_now=True)is_delete = models.BooleanField(default=False)# 轮播图
class Banner(models.Model):img = models.ImageField(upload_to="banner", default="banner1.png", verbose_name="图片")order = models.IntegerField(verbose_name="顺序")create_time = models.DateTimeField(auto_now=True, verbose_name="创建时间")is_delete = models.BooleanField(default=False, verbose_name="是否删除")class Meta:verbose_name_plural = "轮播图"# 公告
class Notice(models.Model):title = models.CharField(max_length=64, verbose_name="公共标题")content = models.TextField(verbose_name="内容")img = models.ImageField(upload_to="notice", default="notice.png", verbose_name="公告图片")create_time = models.DateTimeField(auto_now=True, verbose_name="创建时间")class Meta:verbose_name_plural = "公告表"

admin

from django.contrib import admin# Register your models here.
from .models import Welcome, Banner, Noticeadmin.site.register(Welcome)
admin.site.register(Banner)
admin.site.register(Notice)

serializer

from rest_framework import serializers
from .models import Welcome, Banner, Noticeclass WelcomeSerializer(serializers.ModelSerializer):class Meta:model = Welcomefields = ["img", "link"]# 轮播图表序列化类
class BannerSerializer(serializers.ModelSerializer):class Meta:model = Bannerfields = "__all__"# 社区通知序列化类
class NoticeSerializer(serializers.ModelSerializer):class Meta:model = Noticefields = ["id", "title"]

url

from django.contrib import admin
from django.urls import path, include
from .views import WelcomeView, BannerView
from rest_framework.routers import SimpleRouterrouter = SimpleRouter()
router.register("welcome", WelcomeView, "welcome")
router.register("banner", BannerView, "banner")
urlpatterns = [path("", include(router.urls))]

信息采集拍照上传

# 1 小程序端拍照---》上传到咱们后台--》后台保存一份(media)--》把图片---》传到百度ai人脸识别库中存着# 2 学习-百度ai人脸库上传-百度ai人脸库删除-百度ai人脸库根据人脸匹配

人脸识别(上传-删除-查询)

详情见 https://ai.baidu.com/ai-doc/FACE/ek37c1qiz

from aip import AipFace
import base64
from pypinyin import lazy_pinyin, Style
class BaiDuAI:def __init__(self,APP_ID='',API_KEY='',SECRET_KEY=''):""" 你的 APPID AK SK """self.APP_ID = APP_IDself.API_KEY = API_KEYself.SECRET_KEY = SECRET_KEYself.client = AipFace(self.APP_ID, self.API_KEY, self.SECRET_KEY)def name_to_pinyin(self,text):style = Style.TONE3name_list=lazy_pinyin(text, style=style)return ''.join(name_list)def add_user(self):image = base64.b64encode(open('./彭于晏.jpg','rb').read()).decode('utf-8')imageType = "BASE64"groupId = "100"userId=self.name_to_pinyin('彭于晏')""" 调用人脸注册 """res=self.client.addUser(image, imageType, groupId, userId)print(res)""" 如果有可选参数 """# options = {}# options["user_info"] = "彭于晏"# options["quality_control"] = "NORMAL"# options["liveness_control"] = "LOW"# options["action_type"] = "REPLACE"## """ 带参数调用人脸注册 """# self.client.addUser(image, imageType, groupId, userId, options)'''{'error_code': 0, 'error_msg': 'SUCCESS', 'log_id': 937906163, 'timestamp': 1716948937, 'cached': 0, 'result': {'face_token': '7598884f9d9a349218941e8e6f52c884', 'location': {'left': 493.4, 'top': 329.74, 'width': 348, 'height': 321, 'rotation': -8}}}'''def search(self):image = base64.b64encode(open('./pyy.jpeg', 'rb').read()).decode('utf-8')imageType = "BASE64"groupIdList = "100,2"""" 调用人脸搜索 """res=self.client.search(image, imageType, groupIdList);""" 如果有可选参数 """# options = {}# options["match_threshold"] = 70# options["quality_control"] = "NORMAL"# options["liveness_control"] = "LOW"# # options["user_id"] = "233451"# options["max_user_num"] = 3## """ 带参数调用人脸搜索 """# res=self.client.search(image, imageType, groupIdList, options)print(res)'''{'error_code': 0, 'error_msg': 'SUCCESS', 'log_id': 1190062038, 'timestamp': 1716949190, 'cached': 0, 'result': {'face_token': 'bdbc6214eb18ccee1bf72d1f72f0c979', 'user_list': [{'group_id': '100', 'user_id': 'peng2yu2yan4', 'user_info': '', 'score': 14.822490692139}]}}'''def delete(self):userId = ""groupId = ""faceToken = """"" 调用人脸删除 """res=self.client.faceDelete(userId, groupId, faceToken);print(res)'''{'error_code': 0, 'error_msg': 'SUCCESS', 'log_id': 1355696697, 'timestamp': 1716949355, 'cached': 0, 'result': None}'''
if __name__ == '__main__':ai=BaiDuAI()# ai.add_user()# ai.search()ai.delete()

后端接口

# 1 采集上传接口---》存本地--》存百度ai
# 2 查询今天采集数据--》1 当前用户(没实现) 2 当天采集的数据  3 总条数Collection.objects.all().filter(create_time__gte=datetime.now().date())
# 3 删除采集接口-->id号删除

views

class CollectionView(GenericViewSet, ListModelMixin, DestroyModelMixin, CreateModelMixin):# 查出当天的--》没过滤当前用户queryset = Collection.objects.all().filter(create_time__gte=datetime.now().date())serializer_class = CollectionSerializerdef get_serializer_class(self):if self.action == 'create':return CollectionSaveSerializerelse:return CollectionSerializerdef list(self, request, *args, **kwargs):# 过滤当前用户采集的--》多种类型用户,数据权限不一样res = super().list(request, *args, **kwargs)today_count = len(self.get_queryset())return Response({'code': 100, 'msg': '成功', 'result': res.data, 'today_count': today_count})## 删除人脸,没有删除 具体的图片--》定时任务--》每天晚上备份用户头像def destroy(self, request, *args, **kwargs):from libs.baidu_ai import BaiDuFaceinstance = self.get_object()# 百度ai中删除baidu = BaiDuFace()res = baidu.delete(instance.name_pinyin, instance.face_token)print(res)self.perform_destroy(instance)return Response()# def perform_destroy(self, instance):#     from libs.baidu_ai import BaiDuFace#     baidu = BaiDuFace()#     res = baidu.delete(instance.name_pinyin, instance.face_token)#     print(res)#     super().perform_destroy(instance)

urls

router.register('collection', CollectionView, 'collection') # 3个接口## 3个接口######
http://192.168.1.96:8000/api/v1/collection/  # get 获取数据
http://192.168.1.96:8000/api/v1/collection/  # post 提交采集
http://192.168.1.96:8000/api/v1/collection/1/  # delete 删除

serializer

# 上传人脸序列化类
class CollectionSaveSerializer(serializers.ModelSerializer):class Meta:model = Collectionfields = ['name', 'avatar', 'area']def create(self, validated_data):# 在百度ai注册from libs.baidu_ai import BaiDuFacebaidu=BaiDuFace()avatar_file_object = validated_data.get('avatar')print(avatar_file_object)name = validated_data.get('name')name_pinyin=baidu.name_to_pinyin(name)res=baidu.add_user(avatar_file_object,name,name_pinyin)validated_data['name_pinyin'] = name_pinyinvalidated_data['face_token'] = res.get('result').get('face_token')instance=super().create(validated_data)return instance
# 采集序列化类
class CollectionSerializer(serializers.ModelSerializer):  # 查询所有序列化类class Meta:model = Collectionfields = ['id', 'name', 'avatar', 'area']depth = 1  # area 外键关联详情拿到

image-20240529154533105

采集列表页面 collection

wxml

<view class="container"><view class="top"><view class="tip">今日采集数量(人)</view><view class="count">{{dataDict.today_count}}</view></view><view class="function"><view class="menu" style="border-right:1rpx solid #ddd;" bindtap="bindToForm"> <text class="iconfont icon-xinxicaiji"></text> 信息采集</view><view class="menu" bindtap="bindToStatistics">  <text class="iconfont icon-shujutongji" ></text> 数据统计</view></view><view class="table"><view class="item"><view class="title">社区信息列表({{dataDict.today_count}}人)</view></view><view class="item" wx:for="{{dataDict.result}}" wx:for-item="row" wx:key="index"><view class="record"><view class="avatar"><image src="{{row.avatar}}"></image></view><view class="desc"><view class="username">{{row.name}}</view><view><view class="txt-group"><label class="zh">网格区域</label><label class="en"> | {{row.area.desc}}</label></view><view class="area"> <label class="fa fa-map-marker"></label> {{row.area.name}} </view></view></view><view class="delete" bindtap="doDeleteRow" data-nid="{{row.id}}" data-index="{{index}}" ><label class="iconfont icon-shanchu"></label></view></view></view></view>
</view>

wxss

.top {background-color: #01ccb6;height: 200rpx;display: flex;flex-direction: column;align-items: center;color: white;
}.top .tip {font-size: 22rpx;font-weight: 100;
}.top .count {padding: 10rpx;font-size: 58rpx;
}.function {display: flex;flex-direction: row;justify-content: space-around;background-color: #02bfae;
}.function .menu {font-size: 28rpx;margin: 25rpx 0;color: white;width: 50%;text-align: center;flex-direction: row;flex-direction: column;align-items: center;
}.table .item {border-bottom: 1rpx solid #e7e7e7;}.table .item .title{margin: 20rpx 30rpx;padding-left: 10rpx;border-left: 5rpx solid #02bfae;font-size: 26rpx;
}.record{margin: 30rpx 40rpx;display: flex;flex-direction: row;justify-content: flex-start;
}.record .avatar{width: 200rpx;height: 200rpx;
}.record .avatar image{width: 100%;height: 100%;border-radius: 30rpx;
}.record .desc{margin: 0 40rpx;
}
.desc{width: 290rpx;display: flex;flex-direction: column;justify-content: space-between;
}
.desc .username{margin-top: 25rpx;font-size: 38rpx;
}.txt-group{font-size: 27rpx;margin: 10rpx 0;
}
.txt-group .zh{color: #8c8c8c;
}.txt-group .en{color: #cccccc;
}.area{color: #00c8b6;font-weight: bold;
}.delete{width: 100rpx;color: red;text-align: center;display: flex;flex-direction: column;justify-content: center;
}

js

import api from '../../../static/js/api.js'
Page({data: {dataDict: {result: [],today_count: 0,}},onLoad(options) {//发送网络请求... this.refresh();},onShow(){this.refresh();},refresh() {//1.发送网络请求//2.数据绑定wx.showLoading({mask: true})wx.request({url: api.collection,method: "GET",success: (res) => {this.setData({dataDict: res.data})},complete() {wx.hideLoading()}})},bindToForm(){wx.navigateTo({url: '/pages/second/form/form',})},doDeleteRow(e){wx.showModal({title: '确认是否删除?',confirmColor: "#ff461f",success: (res) => {if (!res.confirm) {return}var nid = e.currentTarget.dataset.nidwx.showLoading({title: '删除中',mask:true})wx.request({url: api.collection + nid + '/',method:'DELETE',success:(res) =>{this.refresh()},complete() {wx.hideLoading()}})}})},
})

信息采集form页

html

<view class="avatar"><image src='{{avatar}}' bindtap="bindToCamera"></image>
</view><view class="form"><view class="row-group"><input placeholder="请填写姓名" placeholder-class='txt' model:value="{{name}}" bindinput="bindNameChange" /></view><view class="picker-group"><picker bindchange="bindPickerChange" value="{{index}}" range="{{objectArray}}" range-key="name"><view wx:if="{{ index > -1}}" class="picker-txt picker">当前网格:{{objectArray[index].name}}</view><view wx:else class="picker-txt" >请选择网格</view></picker></view><view><button class="submit" bindtap="postUser" > 提 交 </button></view>
</view>

wxss

.avatar{display: flex;flex-direction: column;align-items: center;
}.avatar image {margin-top: 140rpx;width: 300rpx;height: 300rpx;border-radius: 30rpx;border: 1px solid #ddd;
}.form{padding: 40rpx;
}.form .row-group{padding: 10rpx 0;border-bottom: 1rpx solid #ddd;position: relative;margin-top: 30rpx;
}.form .row-group text{font-size: 28rpx;padding:20rpx 0;
}.form .row-group input{padding: 10rpx 0;
}.form .row-group .txt{color: #ccc;font-size: 28rpx;
}.form .picker-group{border-bottom: 1rpx solid #ddd;
}.form .picker-group .picker-txt{color: #ccc;font-size: 28rpx;padding: 40rpx 0 20rpx 0;
}.form .picker-group .picker{color: black;
}.form .submit{margin-top: 80rpx;color: #fff;border: 2rpx solid #00c8b6;background-color: #00c8b6;font-size: 32rpx;font-weight: bold;
}

js

.avatar{display: flex;flex-direction: column;align-items: center;
}.avatar image {margin-top: 140rpx;width: 300rpx;height: 300rpx;border-radius: 30rpx;border: 1px solid #ddd;
}.form{padding: 40rpx;
}.form .row-group{padding: 10rpx 0;border-bottom: 1rpx solid #ddd;position: relative;margin-top: 30rpx;
}.form .row-group text{font-size: 28rpx;padding:20rpx 0;
}.form .row-group input{padding: 10rpx 0;
}.form .row-group .txt{color: #ccc;font-size: 28rpx;
}.form .picker-group{border-bottom: 1rpx solid #ddd;
}.form .picker-group .picker-txt{color: #ccc;font-size: 28rpx;padding: 40rpx 0 20rpx 0;
}.form .picker-group .picker{color: black;
}.form .submit{margin-top: 80rpx;color: #fff;border: 2rpx solid #00c8b6;background-color: #00c8b6;font-size: 32rpx;font-weight: bold;
}

拍照页面camera

wxml

<camera class="camera"  device-position="{{ backFront ? 'back' : 'front' }}"  flash="off" frame-size="medium" ></camera><view class="function"><view class="switch"> </view><view class="record" bindtap="takePhoto"><image src="/static/img/camera/record_on.png"></image></view><view class="switch" bindtap="switchCamera"><image src="/static/img/camera/rotate-camera-white.png"></image></view></view>

wxss

page{height: 100%;
}
.camera{height: 80%;width: 100%;
}.function{height: 20%;background-color: black;display: flex;flex-direction: row;justify-content: space-around;align-items: center;
}
.record image{width: 160rpx;height: 160rpx;
}.switch{color: white;width: 80rpx;height: 80rpx;
}
.switch image{width: 80rpx;height: 80rpx;
}

js

// pages/second/camera/camera.js
Page({/*** 页面的初始数据*/data: {backFront:true},takePhoto(){const ctx = wx.createCameraContext()ctx.takePhoto({quality: 'high',success: (res) => {    // 获取照片 //console.log(res);// 对上一个页面中的值,进行修改var pages = getCurrentPages();var prevPage = pages[pages.length - 2];// 把上个页面的图片位置设为刚刚拍的照prevPage.setData({avatar: res.tempImagePath})//跳转回上一个页面  [v1,v2,v2]wx.navigateBack({});}})},switchCamera(e) {var old = this.data.backFrontthis.setData({backFront: !old})},
})

查询所有网格接口

不完善-当前用户网格

#### 路由####
router.register('area', AreaView, 'area')## 视图类####
class AreaView(GenericViewSet, ListModelMixin):queryset = Area.objects.all()serializer_class = AreaSerializer# 配置一个过滤类--》在过滤类中对 queryset进行过滤---》过滤出当前用户的网格
## 序列化类#### 
class AreaSerializer(serializers.ModelSerializer):class Meta:model = Areafields = ['id', 'name', 'desc']
#### 网格数据录入使用admin####

信息采集统计

前端

wxml

<view class="container"><view class="menu" wx:for="{{dataList}}" wx:key="index"><view> <label class="iconfont  icon-SCHEDULE" ></label>   {{item.date}}</view><label>{{item.count}}个</label></view>
</view>

wxss

.container{border-top: 1px solid #ddd;
}.container .menu{font-size: small;padding: 10px 40rpx;border-bottom: 1px dotted #ddd;text-align: center;display: flex;flex-direction: row;justify-content: space-between;background-color: white;
}

js

import settings from '../../../static/js/settings.js'
Page({/*** 页面的初始数据*/data: {dataList:[{'date':'2024年4月20日','count':22},{'date':'2024年4月21日','count':12},{'date':'2024年4月22日','count':232}]},getRecord:function(){wx.showLoading({mask:true})wx.request({url: settings.statistics,method:"GET",success :(res) =>{this.setData({dataList:res.data})},complete:()=>{wx.hideLoading()}})},/*** 生命周期函数--监听页面加载*/onLoad(options) {this.getRecord();},/*** 页面相关事件处理函数--监听用户下拉动作*/onPullDownRefresh() {this.getRecord();},
})

json

{"usingComponents": {},"navigationBarTitleText": "采集统计","enablePullDownRefresh": true
}

后端

views

class StatisticsView(GenericViewSet, ListModelMixin):queryset = Collection.objects.annotate(date=Trunc('create_time', 'day')).values('date').annotate(count=Count('id')).values('date', 'count')serializer_class = StatisticsListSerializer

serializer

# 采集统计序列化类
class StatisticsListSerializer(serializers.Serializer):date = serializers.DateTimeField(format="%Y年%m月%d日")count = serializers.IntegerField()

models

class Collection(models.Model):name = models.CharField(max_length=32, verbose_name='采集人员姓名')name_pinyin=models.CharField(max_length=32, verbose_name='姓名拼音',null=True)avatar = models.ImageField(upload_to='collection/%Y/%m/%d/', default='default.png', verbose_name='头像')create_time = models.DateTimeField(verbose_name='采集时间',default=datetime.now())face_token=models.CharField(max_length=128, verbose_name='百度ai的Token',null=True)area = models.ForeignKey(to='Area', null=True, verbose_name='网格区域', on_delete=models.CASCADE)class Meta:verbose_name_plural = '采集表'def __str__(self):return self.name

image-20240529154807975

人脸检测功能

前端

<view class="header"><camera class="camera" device-position="{{ backFront ? 'back' : 'front' }}" flash="off" frame-size="medium"></camera><view class="switch" bindtap="switchCamera"><image src="/images/camera/rotate-camera-white.png"></image></view><button class="submit" bindtap="takePhoto"> 拍照检测 </button>
</view><view class="table"><view class="item"><view class="title">检测记录</view></view><view class="item" wx:for="{{record}}" wx:for-item="row" wx:key="index"><view class="record"><view class="avatar"><image src="{{row.avatar}}"></image></view><view class="desc"><view wx:if="{{row.code == 100}}" class="username">检测成功:{{row.user_id}}</view><view wx:else class="username">检测失败:{{row.msg}}</view><view><view class="txt-group"><label class="zh">{{row.error_msg}}</label></view></view></view><view class="delete"><block wx:if="{{row.code == 100}}"><label class="iconfont icon-ziyuanxhdpi" style="color:green"></label></block><block wx:else><label class="iconfont icon-ziyuanxhdpi" style="color:red"></label></block></view></view></view>
</view>

css

/* pages/face/face.wxss */
.header{position: relative;
}
.camera{height: 600rpx;width: 100%;
}.switch{position: absolute;top: 10rpx;right: 20rpx;height: 80rpx;width: 80rpx;
}.switch image{height: 100%;width: 100%;
}.submit{margin-top: 40rpx;color: #fff;border: 2rpx solid #00c8b6;background-color: #00c8b6;font-size: 32rpx;font-weight: normal;
}.table{margin-top: 40rpx;border-top: 1rpx solid #e7e7e7;
}.table .item {border-bottom: 1rpx solid #e7e7e7;}.table .item .title{margin: 20rpx 30rpx;padding-left: 10rpx;border-left: 5rpx solid #02bfae;font-size: 26rpx;
}.record{margin: 10rpx 40rpx;display: flex;flex-direction: row;justify-content: space-between;
}.record .avatar{width: 100rpx;height: 100rpx;
}.record .avatar image{width: 100%;height: 100%;border-radius: 30rpx;
}.record .desc{margin: 0 40rpx;
}
.desc{width: 290rpx;display: flex;flex-direction: column;justify-content: space-around;
}
.desc .username{font-size: 25rpx;
}.txt-group{font-size: 20rpx;margin: 5rpx 0;
}
.txt-group .zh{color: #8c8c8c;
}.txt-group .en{color: #cccccc;
}.area{color: #00c8b6;font-weight: bold;
}.delete{width: 100rpx;text-align: center;display: flex;flex-direction: column;justify-content: center;
}

js

import settings from '../../../static/js/settings.js'Page({data: {backFront:true,record:[]},switchCamera(e) {var old = this.data.backFrontthis.setData({backFront: !old})},takePhoto(e){wx.showLoading({title: '检测中',mask:true})const ctx = wx.createCameraContext()ctx.takePhoto({quality: 'high',success: (res) => {wx.uploadFile({url: settings.face,filePath: res.tempImagePath,name: 'avatar',success:(response)=>{let resdata = JSON.parse(response.data)console.log(resdata)if(resdata.code==100 || resdata.code==102){console.log(resdata)resdata.avatar = res.tempImagePathvar oldRecord = this.data.recordoldRecord.unshift(resdata)console.log(oldRecord)this.setData({record:oldRecord})}else{wx.showToast({title: '请正常拍照'})}},complete:function(){wx.hideLoading()}})}})},
})

json

{"usingComponents": {},"navigationBarTitleText": "人脸检测"
}

后端

# 人脸检测接口
class FaceView(GenericViewSet):def create(self, request, *args, **kwargs):avatar_object = request.data.get('avatar')if not avatar_object:return Response({"msg": "未提交图像", "code": 101})from libs.baidu_ai import BaiDuAIai = BaiDuAI()result = ai.search(avatar_object)if result.get('error_code') == 0:  # 查询到# {'error_code': 0, 'error_msg': 'SUCCESS', 'log_id': 2159604393, 'timestamp': 1713864959, 'cached': 0, 'result': {'face_token': '095994eca64424cee347b59e0a7edc0e', 'user_list': [{'group_id': '100', 'user_id': 'li3si1xian4', 'user_info': '', 'score': 98.035797119141}]}}user = result.get('result').get('user_list')[0]user_info = user.get('user_info')user_id = user.get('user_id')score = user.get('score')return Response({"code": 100, 'msg': '匹配成功', 'user_info': user_info, 'user_id': user_id, 'score': score,'avatar':''})else:return Response({"code": 102, 'msg': '匹配失败,该人员可能不是我社区人员,注意防范'})

baidu_api

...

image-20240529155244440

语音识别

前端

wxml

<textarea class="text" placeholder="等待语音识别自动录入..." placeholder-class="hoder" model:value="{{content}}" maxlength="{{-1}}"></textarea><button class="btn" hover-class="press" bind:longpress="recordStart" bind:touchcancel="recordCancel" bind:touchend="recordStop"> <label class="fa fa-microphone"></label> 按住说话</button>

wxss

page{background-color: #f5f5f5;
}
.text{height: 400rpx;background-color: white;width: 100%;padding: 20rpx;
}.btn{margin-top: 30rpx;/* color: #fff; */border: 2rpx solid #ddd;background-color: white; font-size: 32rpx;font-weight: normal;
}.press label{color: #179B16;
}
.press{background-color: #ddd;
}
.hoder{font-size: 28rpx;
}#####json####
{"usingComponents": {},"navigationBarTitleText": "语音识别"
}

js

const recorderManager = wx.getRecorderManager()
import settings from '../../../static/js/settings.js'Page({/*** 页面的初始数据*/data: {content:"",record:false},recordStart:function(){this.setData({record:true})const options = {// duration: 6000,//指定录音的时长,单位 mssampleRate: 16000,//采样率numberOfChannels: 1,//录音通道数encodeBitRate: 48000,//编码码率format: 'wav'//音频格式,有效值 }//开始录音recorderManager.start(options)},recordCancel:function(){console.log("停止");this.setData({record:false})wx.hideLoading()},recordStop:function(){if(!this.data.record){return}recorderManager.stop();recorderManager.onStop((res) => {// this.tempFilePath = res.tempFilePathwx.showLoading()wx.uploadFile({filePath: res.tempFilePath,name: 'voice',url: settings.voice,success:(response)=>{console.log(response)// {'code': 100, 'msg':'成功','result': ['欢迎欢迎']}let voiceResponse = JSON.parse(response.data)if(voiceResponse.code == 100){console.log(voiceResponse)this.setData({content:this.data.content + voiceResponse.result[0]})}else{wx.showToast({title: '识别失败,请重新操作!',icon: "none"})}},complete:()=>{wx.hideLoading()}},)})this.setData({record:false})},
})

后端

baidu_ai

# pip install baidu-aip
from aip import AipSpeech
import base64# 语音识别
class BaiDuVoice:def __init__(self,APP_ID="",API_KEY="",SECRET_KEY="",):"""你的 APPID AK SK"""self.APP_ID = APP_IDself.API_KEY = API_KEYself.SECRET_KEY = SECRET_KEYself.client = AipSpeech(self.APP_ID, self.API_KEY, self.SECRET_KEY)def speed(self, voice_object):res = self.client.asr(voice_object.read(),"pcm",16000,{"dev_pid": 1537,},)return res

views

from libs.baidu_ai import BaiDuVoiceclass VoiceView(GenericViewSet):def create(self, request, *args, **kwargs):voice_object = request.data.get('voice')# with open('./a.wav','wb') as f:#     f.write(voice_object.read())ai = BaiDuVoice()result = ai.speed(voice_object)# {'corpus_no': '6847771638436561158', 'result': ['你是不是打过来?'], 'sn': '15921476781594371078', 'err_msg': 'success.', 'err_no': 0}if result.get('err_no') == 0:return Response({'code': 100, 'msg': '识别成功', 'result': result.get('result')})else:return Response({'code': 101, 'msg': '识别失败'})

image-20240530210822410

活动页面

前端

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" bindtap="handleSignup">报名</button></view>
</view>

wxss

.container {padding: 20rpx;
}.activity-item {display: flex;align-items: flex-start;justify-content: space-between;margin-bottom: 20rpx;border-bottom: 1px solid #ebebeb;padding-bottom: 20rpx;
}.activity-content {flex: 1;
}.activity-title {font-size: 28rpx;font-weight: bold;margin-bottom: 10rpx;
}.activity-time {font-size: 24rpx;color: #666666;margin-bottom: 10rpx;
}.activity-enrollment {font-size: 24rpx;color: #999999;margin-bottom: 10rpx;
}.activity-description {font-size: 24rpx;color: #333333;margin-top: 10rpx;white-space: pre-wrap; /* 自动换行 */
}.signup-btn {background-color: #50c8ff;color: #ffffff;border: none;border-radius: 4rpx;padding: 10rpx 20rpx;font-size: 24rpx;
}

js

var app = getApp();
import settings from '../../static/js/settings.js'
Page({data: {activityList: []},onLoad: function () {// 页面加载时执行的逻辑this.refresh()},refresh(){wx.showLoading({mask: true})wx.request({url: settings.activity,method: "GET",success: (res) => {this.setData({activityList: res.data})},complete() {wx.hideLoading()}})},handleSignup: function (event) {// 处理报名按钮点击事件var index = event.currentTarget.dataset.index; // 获取当前点击的活动索引console.log('点击了报名按钮,索引为:', index);}
})

后端

views

class ActivityView(GenericViewSet,ListModelMixin):queryset =Activity.objects.all().order_by('date')serializer_class = ActivitySerializer

serializer

class ActivitySerializer(serializers.ModelSerializer):class Meta:model = Activityfields = ['id', 'title','text','date','count','score','total_count']extra_kwargs={'date':{'format':"%Y-%m-%d"}}

models

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)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)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 = '活动报名记录'

image-20240530210808124

公告页面

前端

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>

wxss

.container {padding: 20rpx;
}.notice-item {display: flex;align-items: flex-start;margin-bottom: 20rpx; /* 添加间距 */border-bottom: 1px solid #f0f0f0; /* 添加底部边框 */padding-bottom: 20rpx; /* 增加底部内边距 */
}.notice-image {width: 150rpx;height: 120rpx;border-radius: 6rpx;margin-right: 20rpx;
}.notice-content {flex: 1;
}.notice-title {font-size: 28rpx;font-weight: bold;margin-bottom: 10rpx;
}.notice-time {font-size: 24rpx;color: #666666;margin-bottom: 10rpx;
}.notice-details {font-size: 24rpx;color: #333333;
}

js

import settings from '../../static/js/settings.js'
Page({data: {noticeList: [{title: '公告标题1',create_time: '2024-04-25',content: '公告内容描述1,公告内容描述1,公告内容描述1。', // 可以根据实际情况添加更多内容igm: '/images/notice/notice1.jpg' // 图片路径,根据实际情况修改},{title: '公告标题2',create_time: '2024-04-26',content: '公告内容描述2,公告内容描述2,公告内容描述2。', // 可以根据实际情况添加更多内容igm: '/images/notice/notice2.jpg' // 图片路径,根据实际情况修改},// 可以添加更多社区公告数据]},onLoad: function () {// 页面加载时执行的逻辑this.refresh()},refresh(){wx.showLoading({mask: true})wx.request({url: settings.notice,method: "GET",success: (res) => {this.setData({noticeList: res.data})},complete() {wx.hideLoading()}})}
})

后端

views

class NoticeView(GenericViewSet,ListModelMixin):queryset =Notice.objects.all().order_by('create_time')serializer_class = NoticeSerializer

serializer

class NoticeSerializer(serializers.ModelSerializer):class Meta:model = Noticefields = ['id', 'title','img','create_time','content']extra_kwargs={'create_time':{'format':"%Y-%m-%d"}}

image-20240530210758857

登陆功能

前端

个人中心页面

<block wx:if="{{userInfo==null}}">
<view class="container1"><view class="main"><view class="icon-view"><!-- 应用图标 --><image src="/static/img/icon/icon.png" class="app-icon"></image><text class="title">智慧社区</text></view></view><van-cell-group><van-cell><button type="warn" open-type="getPhoneNumber" bindgetphonenumber="getPhoneNumber">手机号快捷登录</button></van-cell></van-cell-group><!-- 其他手机号登录 --><van-cell-group><van-cell><button type="primary" plain bindtap="handleOtherLogin">其他手机号登录</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 bindtap="logout">{{userInfo.name}}</view></view></view></view><view class="numbers"><view class="row"><text>{{userInfo.score}}</text><text>积分</text></view><view class="row"><text>55</text><text>其他</text></view><view class="row"><text>77</text><text>其他</text></view><view class="row"><text>56</text><text>其他</text></view></view></view><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>

wxss

page{height: 100%;
}.login-area{height: 100%;display: flex;flex-direction: column;justify-content: center;align-items: center;
}
.login-area .btn{width: 200rpx;height: 200rpx;border-radius: 500%;background-color: #5cb85c;color: white;display: flex;flex-direction: row;align-items: center;justify-content: center;
}.user-area{height: 100%;display: flex;flex-direction: column;justify-content: center;align-items: center;
}
.user-area image{width: 200rpx;height: 200rpx;border-radius: 500%;
}
.user-area .name{font-size: 30rpx;padding: 30rpx 0;
}.user-area .logout{color: #a94442;
}.top-view{background-color: #01ccb6;color: white;padding: 40rpx;
}.top-view .user{display: flex;flex-direction: row;justify-content: space-between;align-items: center;
}
.top-view .user .row{display: flex;flex-direction: row;justify-content: flex-start;align-items: center;
}
.top-view .user .avatar{width: 100rpx;height: 100rpx;border-radius: 50%;
}.top-view .user .name{display: flex;flex-direction: row;justify-content: flex-start;padding-left: 20rpx;
}
.top-view .user .name navigator{padding: 0 5rpx;
}.top-view .site{background-color: rgba(0, 0, 0, 0.16);padding: 20rpx;border-top-left-radius: 32rpx;border-bottom-left-radius: 32rpx;
}.top-view .numbers{display: flex;flex-direction: row;justify-content: space-between;font-size: 28rpx;padding: 40rpx;padding-bottom: 0rpx;
}.top-view .numbers .row{display: flex;flex-direction: column;align-items: center;
}/* login.wxss */
.container1 {padding: 20rpx;
}
.main{display: flex;justify-content: center;align-items: center;
}
.icon-view{display: flex;flex-direction: column;margin-bottom: 50rpx;
}.app-icon {width: 100rpx;height: 100rpx;margin: 40rpx auto 20rpx; /* 上边距为40rpx,下边距为20rpx,左右居中 */
}.quick-login-header {display: flex;align-items: center;
}.icon {width: 40rpx;height: 40rpx;margin-right: 20rpx;
}.title {font-size: 28rpx;font-weight: bold;color: #333333;
}.divider {height: 20rpx;
}.login-option {font-size: 28rpx;color: #333333;
}.login-option .van-cell__icon {color: #07c160;
}.agreement-container {display: flex;align-items: center;margin-top: 20rpx;
}.checkbox {margin-right: 10rpx;
}.agreement-text {font-size: 24rpx;color: #666666;
}.agreement-link {font-size: 24rpx;color: #07c160;
}

js

var app = getApp(); // 拿到的是 app.js中data的数据
import settings from '../../static/js/settings.js'
Page({data: {userInfo: null,},getPhoneNumber(event) {console.log(event)// 通过获取手机号返回的code--传递给后端--后端调用:POST https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=ACCESS_TOKEN -->获取手机号--》后端签发token给前端wx.request({url: settings.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.tokenvar name = data.namevar score = data.scorevar avatar = data.avatarapp.initUserInfo(name, score, avatar, token)var info = app.globalData.userInfoconsole.log('globalData.userInfo', info)if (info) {this.setData({userInfo: info})}} else {wx.showToast({title: '登录失败',})}}})},handleOtherLogin(e) {wx.navigateTo({url: '/pages/second/otherlogin/otherlogin'})},onShow() {var info = app.globalData.userInfoconsole.log('globalData.userInfo', info)if (info) {this.setData({userInfo: info})}},handleLogout() {app.logoutUserInfo()this.setData({userInfo: null})}
})

app.js

// app.js
App({globalData: {userInfo: null},initUserInfo: function (name, score, avatar, token) {var info = {name: name,score: score,avatar: avatar,token: token};this.globalData.userInfo = infowx.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}
})

登录页面

wxml

<view class="container"><view class="main"><view class="icon-view"><!-- 应用图标 --><image src="/static/img/icon/icon.png" class="app-icon"></image><text class="title">智慧社区</text></view></view><van-field value="{{ phone }}" bind:input="onPhoneInput" label="手机号" type="tel" placeholder="请输入手机号" clearable="{{ true }}" /><van-field value="{{code}}" bind:input="onCodeInput" center clearable label="验证码" placeholder="请输入验证码" use-button-slot><van-button slot="button" size="small" type="primary" bind:tap="sendCode" disabled='{{sendCodeDisabled}}'>{{buttonText}}</van-button></van-field><van-button type="info" block="{{ true }}" bind:tap="login">登录</van-button>
</view>

wxss

.container {padding: 20rpx;}
.main{display: flex;justify-content: center;align-items: center;
}
.icon-view{display: flex;flex-direction: column;margin-bottom: 50rpx;
}
.title {font-size: 28rpx;font-weight: bold;color: #333333;
}
.app-icon {width: 100rpx;height: 100rpx;margin: 40rpx auto 20rpx; /* 上边距为40rpx,下边距为20rpx,左右居中 */
}

js

import settings from '../../../static/js/settings.js'
var app = getApp()
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: settings.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: '请输入手机号',})}},// 登录login() {// 在这里编写登录逻辑,此处仅做示例console.log('登录');if(this.data.phone&&this.data.code){wx.request({url: settings.login,method:'POST',data:{mobile:this.data.phone,code:this.data.code},success:(res)=>{var data = res.data;console.log(data)if (data.code == 100) {console.log('---', data)var token = data.tokenvar name = data.namevar score = data.scorevar avatar = data.avatarapp.initUserInfo(name, score, avatar, token)var info = app.globalData.userInfoconsole.log('globalData.userInfo', info)wx.navigateBack()} else {wx.showToast({title: '登录失败',})}}})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);}
});

后端

views

from rest_framework.decorators import action
from django.core.cache import cache
from faker import Faker
from libs.send_tx_sms import send_sms_by_phone, get_code
from rest_framework_simplejwt.tokens import RefreshTokenclass LoginView(GenericViewSet):@action(methods=['GET'], detail=False)def send_sms(self, request, *args, **kwargs):# 1 取出前端传入手机号mobile = request.query_params.get('mobile')# 2 获取随机验证码code = get_code()# 3 验证码放到缓存cache.set(f'sms_{mobile}', code)# 4 发送短信res = send_sms_by_phone(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 如果用户不存在,创建用户,再签发tokenfake = Faker('zh_CN')username = fake.name()user = UserInfo.objects.create(mobile=mobile, name=username)refresh = RefreshToken.for_user(user)return Response({'code': 100, 'msg': '登录成功', 'token': str(refresh.access_token), 'name': user.name,'score': user.score, 'avatar': 'http://127.0.0.1:8000/media/' + str(user.avatar)})else:return Response({'code': 101, 'msg': '验证码错误'})@action(methods=['POST'], detail=False)def quick_login(self, request, *args, **kwargs):# 1 取出前端传入的codecode = 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://127.0.0.1:8000/media/' + str(user.avatar)})

image-20240530215426185

image-20240530215436628

image-20240530215503582

活动报名功能

前端

activity.js

var app = getApp();
import settings from '../../static/js/settings.js'
Page({data: {activityList: []},onLoad: function () {// 页面加载时执行的逻辑this.refresh()},refresh(){wx.showLoading({mask: true})wx.request({url: settings.activity,method: "GET",success: (res) => {this.setData({activityList: res.data})},complete() {wx.hideLoading()}})},handleSignup: function (event) {var index = event.currentTarget.dataset.mark; // 获取当前点击的活动索引// 1 校验用户是否登录var info = app.globalData.userInfoconsole.log(info)if (info) {//2 处理报名按钮点击事件console.log('点击了报名按钮,索引为:', index);wx.request({url: settings.join,method:'POST',data:{'id':index},header:{token:info.token},success:(res)=>{console.log(res.data)wx.showToast({title: res.data.msg,})}})} else {wx.showToast({title: '请先登录',})}}
})

后端

views

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

Authentication1.py

from .models import UserInfo
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from rest_framework_simplejwt.authentication import JWTAuthenticationclass 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

image-20240530215408361

商城兑换页面

前端

wxml

<van-dropdown-menu active-color="#1989fa"><van-dropdown-item value="{{ value1 }}" options="{{ option1 }}" /><van-dropdown-item value="{{ value2 }}" options="{{ option2 }}" />
</van-dropdown-menu><van-grid column-num="3" border="{{ true }}"><van-grid-item use-slot wx:for="{{ 8 }}" wx:for-item="index" border><image style="width: 100%; height: 90px;" src="https://img.yzcdn.cn/vant/apple-{{ index + 1 }}.jpg" /><view class="desc"><view class="title">{{item.title}}</view><view class="exchange"><view>{{item.price}}积分</view><van-button color="linear-gradient(to right, #4bb0ff, #6149f6)" bindtap="doExchange" data-gid="{{item.id}}" size="mini">兑换</van-button></view></view></van-grid-item></van-grid>

js

import settings from '../../../static/js/settings.js'
var app = getApp()
Page({data: {option1: [{ text: '全部商品', value: 0 },{ text: '最新上架', value: 1 },{ text: '活动商品', value: 2 },],option2: [{ text: '默认排序', value: 'a' },{ text: '好评排序', value: 'b' },{ text: '销量排序', value: 'c' },],value1: 0,value2: 'a',},
})

image-20240530215354510

小程序上线https

# 开发者工具上传
# 小程序后台--》提交审核--》审核通过,全网可用-备案
# 配置好合法域名# 1 小程序后台,配置合法地址
https://mp.weixin.qq.com/wxamp/devprofile/get_profile?token=948853190&lang=zh_CN
# 2 在小程序源码中,访问后台地址,改成
const rootUrl = 'https://www.liuqingzheng.top/smart'# 3 小程序上线-在开发者工具选上传,填入版本号# 4 小程序后台-把刚刚上传的小程序设为体验版-只有允许体验的人才能用(测试人员)# 5 提交审核--》一个周审核-需要备案

后端上线

#1  后端要部署在https地址上
# 2 http和https区别https=http+ssl/tls 证书# 3 部署在https上,需要申请证书-合法机构申请-如果证书过期了,不合法--》浏览器会提示安全
# 4 证书有收费,也有免费-阿里云-七牛云-https://zhuanlan.zhihu.com/p/561907474

nginx

events {worker_connections  1024;
}
http {include       mime.types;default_type  application/octet-stream;sendfile        on;client_max_body_size 20M;server {listen 443 ssl;ssl_certificate /usr/local/nginx/cert/liuqingzheng.top.pem;ssl_certificate_key /usr/local/nginx/cert/liuqingzheng.top.key;server_name  liuqingzheng.top;location / {include uwsgi_params;uwsgi_pass 127.0.0.1:8080;uwsgi_param UWSGI_SCRIPT smart_backend.wsgi;uwsgi_param UWSGI_CHDIR /root/smart_backend/;}location /static {alias /home/static;}}
}# https://www.liuqingzheng.top/api/v1/banner/

安装python3.9

#1 linux 服务器它有些系统服务,使用python2写的,linux操作系统要运行,需要有python2的解释器环境-自带# 2 阿里云云服务装了linux--》自带python3.6# 3 centos7.9-python    python2.7.5  -——》 pip-python3   python3.6.8  --->pip3
# 4 在这个基础上装一个项目需要的3.9-到时候,机器上有3个python解释器环境,每个解释器环境又一一个pip--》千万别乱了-pip 安装--》模块装在哪个解释器中了# 5 如果只想装个python解释器  yum install python---》这样装指定不了版本--》咱们不用这种方式# 6 咱们选择源码编译安装--》下载python解释器源码--》自己编译
#1  源码安装python,依赖一些第三方zlib* libffi-devel,如果不装,编译会报错
yum install openssl-devel bzip2-devel expat-devel gdbm-devel readline-devel sqlite-devel psmisc libffi-devel zlib* libffi-devel  -y# 2 前往用户根目录
cd ~#3 下载  3.9.10 源码 服务器终端
# https://registry.npmmirror.com/binary.html?path=python/
wget https://registry.npmmirror.com/-/binary/python/3.9.10/Python-3.9.10.tgz
# wget https://www.python.org/ftp/python/3.9.16/Python-3.9.16.tgz#4  解压安装包
tar -xf Python-3.9.10.tgz #5 进入目标文件
cd Python-3.9.10#6  配置安装路径:/usr/local/python3
# 把python3.9.10 编译安装到/usr/local/python38路径下
./configure --prefix=/usr/local/python39#6  编译并安装,如果报错,说明缺依赖
yum install openssl-devel bzip2-devel expat-devel gdbm-devel readline-devel sqlite-devel psmisc libffi-devel zlib* libffi-devel  -y
# make只是编译----》可执行文件,没有安装
# 类似于在win上下载了安装包,但是没安装
# make install 安装---》类似于在win上下了安装包,一路下一步安装了,指定安装位置---》/usr/local/python39
make &&  make install#7  建立软连接:/usr/local/python38路径不在环境变量,终端命令 python3,pip3
ln -s /usr/local/python39/bin/python3 /usr/bin/python3.9
ln -s /usr/local/python39/bin/pip3 /usr/bin/pip3.9# 机器上有多个python和pip命令,对应关系如下
python       2.x      pip 
python3      3.6      pip3
python3.9    3.9      pip3.9#8  删除安装包与文件:
rm -rf Python-3.9.10
rm -rf Python-3.9.10.tar.xz

安装nginx

# 软件:反向代理服务器  (搜一下:什么是正向代理,什么是反向代理)  反向带代理服务器- 做请求转发    (前端来了个请求---》打在了80端口上---》转到本地8888端口,或者其他机器的某个端口)- 静态资源代理    前端项目直接放在服务器上某个位置----》请求来了,使用nginx拿到访问的内容,直接返回- 负载均衡       假设来了1000个请求--》打在nginx上,nginx性能很高,能顶住---》只转发到某个django项目,可能顶不住---》集群化的不是3台django---》均匀的打在3台机器上#1 前往用户根目录
cd ~#2 下载nginx 1.24.0wget http://nginx.org/download/nginx-1.24.0.tar.gz#3 解压安装包
tar -xf nginx-1.24.0.tar.gz#4 进入目标文件
cd nginx-1.24.0#5 配置安装路径:/usr/local/nginx
./configure --prefix=/usr/local/nginx --with-http_ssl_module#6 编译并安装make &&  make install#7 建立软连接:终端命令 nginx
ln -s /usr/local/nginx/sbin/nginx /usr/bin/nginx #8 删除安装包与文件:
cd ~
rm -rf nginx-1.13.7
rm -rf nginx-1.13.7.tar.xz#9 测试Nginx环境,服务器运行nginx,本地访问服务器ip
nginx   # 启动nginx服务,监听80端口----》公网ip 80 端口就能看到页面了
服务器绑定的域名 或 ip:80# 静态文件放的路径
/usr/local/nginx/html# 查看进程
ps aux | grep nginx# 关闭和启动
关闭:nginx -s stop 
启动:nginx

安装mysql5.7

# mysql 5.7 
#1 前往用户根目录# pwd  查看我在哪个目录下
# cd 切换到某个路径下cd ~  # 回到家路径#2 下载mysql57 
wget http://dev.mysql.com/get/mysql57-community-release-el7-10.noarch.rpm
# ls  查看当前目录下的文件和文件夹#3 安装mysql57yum -y install mysql57-community-release-el7-10.noarch.rpmyum install mysql-community-server --nogpgcheck -y#4 启动mysql57并查看启动状态
systemctl start mysqld  # 启动mysql服务
systemctl status mysqld  # 查看mysql状态#5 查看默认密码并登录(第一次安装,root密码是随机的)
# 去/var/log/mysqld.log 中过滤出包含 password 的多行
grep "password" /var/log/mysqld.log   # Gxn*u8rqM=ol
mysql -uroot -p#6 修改root密码ALTER USER 'root'@'localhost' IDENTIFIED BY 'Lqz12345?';grant all privileges on *.* to 'root'@'%' identified by 'Lqz12345?';如果还连不上,就是mysql 3306的安全组没开---》防火墙端口没开# 7 远程连接:win---》navicate--》连接成功如果链接不成功,需要在阿里云开启安全组# 8 安装mysqlclient
yum install python3-devel -y
yum install mysql-devel --nogpgcheck -y
pip3.9 install mysqlclient# 9
pip3.9 install urllib3==1.26.15
pip3.9 install chardet#10 
mkdir static
STATIC_ROOT = '/root/smart_backend/static/'

编写uwsgi配置文件

# 项目运行,不是使用 python manage.py runserver 运行,这样运行性能很低,没有并发,开发测试阶段用# 上线项目,需要使用一个性能很高的web服务,c语言写的,uwsgi,并发量高,线上环境用他# 在项目中写uwsgi的配置文件--》用uwsgi软件,根据这个配置文件运行django项目#1 smart.xml
<uwsgi><socket>127.0.0.1:8080</socket><chdir>/home/smart_backend/</chdir><module>smart_backend.wsgi</module><processes>4</processes><daemonize>uwsgi.log</daemonize>
</uwsgi>

上传后端项目到云服务器

# 1 配置文件修改
DEBUG = False
ALLOWED_HOSTS = ['*']
DATABASES = {'default': {'ENGINE': 'django.db.backends.mysql','NAME': 'smart','HOST':'127.0.0.1','PORT':3306,'USER':'root','PASSWORD':'Lqz12345?'}
}
BACKEND_URL='https://www.liuqingzheng.top'
# BACKEND_URL='https://106.14.134.13'
STATIC_URL='/static/'# 2 导出项目依赖,项目根路径下有个 requirements.txt--->放了当前项目所有依赖
pip3 install pipreqs
pipreqs ./ --encoding=utf-8
# requirements.txt
baidu_aip==4.16.13
Django==3.2.22
djangorestframework==3.14.0
djangorestframework_simplejwt==5.3.1
Faker==25.0.1
pypinyin==0.51.0
tencentcloud_sdk_python==3.0.1115
urllib3==1.26.15 
django-simpleui
pillow
chardet# 3 项目压缩 zip # 4 服务器上来到 home 目录
cd /home/
yum install lrzsz unzip -y
rz 上传unzip smart_backend.zip# 5 在服务端,安装项目依赖
cd smart_backend
pip3.9 install -r requirements.txt # 6 服务端链接mysql ,需要装连mysql模块
# pip3.9 install mysqlclient  # linux上装会报错
yum install python3-devel -y
yum install mysql-devel --nogpgcheck -y
pip3.9 install mysqlclient# 7 装uwsig,运行django项目
pip3.9 install uwsgi
ln -s /usr/local/python39/bin/uwsgi /usr/bin/uwsgi# 8 启动uwsgi
uwsgi -x smart.xml  # 等同于原来的 python manage.py runserver 127.0.0.1:8080# 9 查看
ps aux |grep uwsgi# 10 停止
pkill -9 uwsgi

nginx配置--后端接口--(http)

# 1 修改nginx 配置文件
cd /usr/local/nginx/conf
mv nginx.conf nginx.conf.bak
vi nginx.conf  # 敲 a  才能粘贴events {worker_connections  1024;
}
http {include       mime.types;default_type  application/octet-stream;sendfile        on;server {listen 80;server_name  127.0.0.1;charset utf-8;location / {include uwsgi_params;uwsgi_pass 127.0.0.1:8080;uwsgi_param UWSGI_SCRIPT smart_backend.wsgi; uwsgi_param UWSGI_CHDIR /root/smart_backend/;}}
}# 按esc
# 敲 :
# 敲 wq
# 敲 回车# 2 重启nginx即可
nginx -s reload # 之前运行着
# 之前没运行
nginx  # 运行起来了

创建数据库,迁移表,导入数据

# 1 在navicat中创建 smart 库# 2 迁移表python3.9 manage.py makemigrationspython3.9 manage.py migrate
# 3 根据报错,安装缺的模块
pip3.9 install django-simpleui
# 4 报错:python3.9 manage.py makemigrations
pip3.9 install urllib3==1.26.15 # 5 根据报错 :chardet
pip3.9 install chardet# 6 缺pillow
pip3.9 install pillow# 7 去后台admin,录入数据# 8 直接导入一个sql文件----之前项目测试的数据# 9 关闭uwsgi,重启
pkill -9 uwsgi
uwsgi -x smart.xml# 10 对于小程序来讲,接口能用了,但是admin访问,加载不了静态资源-uwsgi不能代理静态资源,需要使用nginx代理

配置admin访问管理后台

# 0 创建文件号
mkdir /home/smart_backend/static
# 1 在项目配置文件中,加入
STATIC_ROOT = '/home/smart_backend/static/'
# 2 执行采集命令--》把静态文件采集到static文件夹下
python3.9 manage.py collectstatic# 3 配置nginx代理
cd /usr/local/nginx/conf events {worker_connections  1024;
}
http {include       mime.types;default_type  application/octet-stream;sendfile        on;server {listen 80;server_name  127.0.0.1;charset utf-8;location / {include uwsgi_params;uwsgi_pass 127.0.0.1:8080;uwsgi_param UWSGI_SCRIPT smart_backend.wsgi;uwsgi_param UWSGI_CHDIR /root/smart_backend/;}location /static {alias /home/smart_backend/static;}}
}#4 重启nginx
nginx -s reload# 5 访问admin即可
http://106.14.134.13/admin

证书

配置https访问

# 1 购买证书-https://zhuanlan.zhihu.com/p/561907474
# 2 申请了证书,会有俩文件keypem# 3 传到服务器上cd /usr/local/nginxmkdir certrz  # 把证书放在这个位置unzip 解压# 保证在/usr/local/nginx/cert 有俩文件# 4 修改nginx的配置文件
events {worker_connections  1024;
}
http {include       mime.types;default_type  application/octet-stream;sendfile        on;client_max_body_size 20M;server {listen 443 ssl;ssl_certificate /usr/local/nginx/cert/liuqingzheng.top.pem;ssl_certificate_key /usr/local/nginx/cert/liuqingzheng.top.key;server_name  liuqingzheng.top;location / {include uwsgi_params;uwsgi_pass 127.0.0.1:8080;uwsgi_param UWSGI_SCRIPT smart_backend.wsgi;uwsgi_param UWSGI_CHDIR /root/smart_backend/;}location /static {alias /home/smart_backend/static;}}
}# 重启nginx
nginx -s reload

app.json

{"pages": ["pages/welcome/welcome","pages/index/index","pages/my/my","pages/activity/activity","pages/notice/notice","pages/second/collection/collection","pages/second/face/face","pages/second/voice/voice","pages/second/heart/heart","pages/second/goods/goods","pages/second/camera/camera","pages/second/form/form","pages/second/statistics/statistics","pages/second/otherlogin/otherlogin","pages/second/store/store"],"window": {"navigationBarBackgroundColor": "#ffffff","navigationBarTextStyle": "black","navigationBarTitleText": "test","backgroundColor": "#eeeeee","backgroundTextStyle": "light"
},
"tabBar": {"selectedColor": "#1c1c1b","position": "bottom","list": [{"pagePath": "pages/index/index","text": "首页","iconPath": "/static/img/icon/home.png","selectedIconPath": "/static/img/icon/home-o.png"},{"pagePath": "pages/activity/activity","text": "活动","iconPath": "/static/img/icon/aid.png","selectedIconPath": "/static/img/icon/aid-o.png"},{"pagePath": "pages/notice/notice","text": "公告","iconPath": "/static/img/icon/circle.png","selectedIconPath": "/static/img/icon/circle-o.png"},{"pagePath": "pages/my/my","text": "我的","iconPath": "/static/img/icon/my.png","selectedIconPath": "/static/img/icon/my-o.png"}]},"componentFramework": "glass-easel","sitemapLocation": "sitemap.json","lazyCodeLoading": "requiredComponents","usingComponents": {"van-button": "@vant/weapp/button/index","van-grid": "@vant/weapp/grid/index","van-grid-item": "@vant/weapp/grid-item/index","van-notice-bar": "@vant/weapp/notice-bar/index","van-field": "@vant/weapp/field/index","van-cell": "@vant/weapp/cell/index","van-cell-group": "@vant/weapp/cell-group/index"}
}

app.wxss

@import "/static/css/iconfont.wxss"

settings.js

const BASE_URL = 'http://192.168.1.38:8000/api/v1/'export default {welcome:BASE_URL + 'welcome/',banner:BASE_URL + 'banner/',collection:BASE_URL + 'collection/',area:BASE_URL + 'area/',statistics:BASE_URL + 'statistics/',face:BASE_URL + 'face/',voice:BASE_URL + 'voice/',activity:BASE_URL + 'activity/',notice:BASE_URL + 'notice/',quick_login:BASE_URL+'user/quick_login/',send_sms:BASE_URL+'user/send_sms/',login:BASE_URL + 'user/login/',join:BASE_URL + 'join/join/',
}

image-20240529155533364

app.js

// app.js
App({globalData: {userInfo: null},initUserInfo: function (name, score, avatar, token) {var info = {name: name,score: score,avatar: avatar,token: token};this.globalData.userInfo = infowx.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}
})

urls.py

from django.contrib import admin
from django.urls import path, include
from .views import (WelcomeView,BannerView,CollectionView,AreaView,StatisticsView,FaceView,VoiceView,ActivityView,NoticeView,LoginView,ActivityJoinView,
)
from rest_framework.routers import SimpleRouterrouter = SimpleRouter()
router.register("welcome", WelcomeView, "welcome")
router.register("banner", BannerView, "banner")
router.register("collection", CollectionView, "collection")
router.register("area", AreaView, "area")
router.register("statistics", StatisticsView, "statistics")
router.register("face", FaceView, "face")
router.register("voice", VoiceView, "voice")
router.register("activity", ActivityView, "activity")
router.register("notice", NoticeView, "notice")
router.register("user", LoginView, "user")
router.register("join", ActivityJoinView, "join")urlpatterns = [path("", include(router.urls))]

admin.py

from django.contrib import admin# Register your models here.
from .models import *admin.site.register(Welcome)
admin.site.register(Banner)
admin.site.register(Notice)
admin.site.register(UserInfo)
admin.site.register(Area)
admin.site.register(Collection)
admin.site.register(Activity)

Authentication1.py

from .models import UserInfo
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from rest_framework_simplejwt.authentication import JWTAuthenticationclass 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

models.py

from django.db import models# 广告表
class Welcome(models.Model):img = models.ImageField(upload_to="welcome", default="slash.png")order = models.IntegerField()link = models.CharField(max_length=32)create_time = models.DateTimeField(auto_now=True)is_delete = models.BooleanField(default=False)# 轮播图
class Banner(models.Model):img = models.ImageField(upload_to="banner", default="banner1.png", verbose_name="图片")order = models.IntegerField(verbose_name="顺序")create_time = models.DateTimeField(auto_now=True, verbose_name="创建时间")is_delete = models.BooleanField(default=False, verbose_name="是否删除")class Meta:verbose_name_plural = "轮播图"# 公告
class Notice(models.Model):title = models.CharField(max_length=64, verbose_name="公共标题")content = models.TextField(verbose_name="内容")img = models.ImageField(upload_to="notice", default="notice.png", verbose_name="公告图片")create_time = models.DateTimeField(auto_now=True, verbose_name="创建时间")class Meta:verbose_name_plural = "公告表"# 采集表
class Collection(models.Model):name = models.CharField(max_length=32, verbose_name="采集人员姓名")# 做为人脸识别的id号name_pinyin = models.CharField(max_length=32, verbose_name="姓名拼音", null=True)avatar = models.ImageField(upload_to="collection/%Y/%m/%d/", default="default.png", verbose_name="头像")create_time = models.DateTimeField(auto_now=True, verbose_name="采集时间")# face_token---->人脸识别的token唯一码face_token = models.CharField(max_length=64, verbose_name="百度Token", null=True)# 区域是外键关联area = models.ForeignKey(to="Area", null=True, verbose_name="网格区域", on_delete=models.CASCADE)class Meta:verbose_name_plural = "采集表"def __str__(self):return self.name# 区域表
class Area(models.Model):name = models.CharField(max_length=32, verbose_name="网格区域名")desc = models.CharField(max_length=32, verbose_name="网格简称")# 跟用户一对多---》一个网格员,可以采集多个网格user = models.ForeignKey(to="UserInfo",on_delete=models.CASCADE,null=True,verbose_name="负责用户",blank=True,)class Meta:verbose_name_plural = "区域表"def __str__(self):return self.name# 用户表
class UserInfo(models.Model):name = models.CharField(verbose_name="姓名", max_length=32)mobile = models.CharField(max_length=11, verbose_name="手机号", null=True)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)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)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 = "活动报名记录"

serializer.py

from rest_framework import serializers
from .models import *class WelcomeSerializer(serializers.ModelSerializer):class Meta:model = Welcomefields = ["img", "link"]# 轮播图表序列化类
class BannerSerializer(serializers.ModelSerializer):class Meta:model = Bannerfields = "__all__"# 社区通知序列化类
class NoticeSerializer(serializers.ModelSerializer):class Meta:model = Noticefields = ["id", "title"]# 上传人脸序列化类
class CollectionSaveSerializer(serializers.ModelSerializer):class Meta:model = Collectionfields = ["name", "avatar", "area"]def create(self, validated_data):# 在百度ai注册from libs.baidu_ai import BaiDuFacebaidu = BaiDuFace()avatar_file_object = validated_data.get("avatar")print(avatar_file_object)name = validated_data.get("name")name_pinyin = baidu.name_to_pinyin(name)res = baidu.add_user(avatar_file_object, name, name_pinyin)validated_data["name_pinyin"] = name_pinyinvalidated_data["face_token"] = res.get("result").get("face_token")instance = super().create(validated_data)return instance# 采集序列化类
class CollectionSerializer(serializers.ModelSerializer):  # 查询所有序列化类class Meta:model = Collectionfields = ["id", "name", "avatar", "area"]depth = 1  # area 外键关联详情拿到# 网格序列化类
class AreaSerializer(serializers.ModelSerializer):class Meta:model = Areafields = ["id", "name", "desc"]### 采集统计
class StatisticsListSerializer(serializers.Serializer):date = serializers.DateTimeField(format="%Y年%m月%d日")count = serializers.IntegerField()class ActivitySerializer(serializers.ModelSerializer):class Meta:model = Activityfields = ["id", "title", "text", "date", "count", "score", "total_count"]extra_kwargs = {"date": {"format": "%Y-%m-%d"}}class NoticeSerializer(serializers.ModelSerializer):class Meta:model = Noticefields = ["id", "title", "img", "create_time", "content"]extra_kwargs = {"create_time": {"format": "%Y-%m-%d"}}

views.py

from django.shortcuts import render
from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import ListModelMixin, DestroyModelMixin, CreateModelMixin
from .serializers import (WelcomeSerializer,BannerSerializer,NoticeSerializer,CollectionSerializer,CollectionSaveSerializer,ActivitySerializer,
)
from .models import Welcome, Notice, Banner, Collection, Activity, UserInfo, JoinRecord
from rest_framework.response import Response
from datetime import datetime
from libs.baidu_ai import BaiDuVoice
from rest_framework.decorators import action
from django.core.cache import cache
from faker import Faker
from libs.send_tx_sms import send_sms_by_phone, get_code
from rest_framework_simplejwt.tokens import RefreshToken
from core.Authentication1 import MyJSONWebTokenAuthentication# 广告
class WelcomeView(GenericViewSet, ListModelMixin):queryset = Welcome.objects.all().filter(is_delete=False).order_by("-order")serializer_class = WelcomeSerializer# 轮播图
class BannerView(GenericViewSet, ListModelMixin):queryset = Banner.objects.all().filter(is_delete=False).order_by("order")[:2]serializer_class = BannerSerializerdef list(self, request, *args, **kwargs):res = super().list(request, *args, **kwargs)notice = Notice.objects.all().order_by("create_time").first()serializer = NoticeSerializer(instance=notice)return Response({"code": 100, "msg": "成功", "banner": res.data, "notice": serializer.data})class CollectionView(GenericViewSet, ListModelMixin, DestroyModelMixin, CreateModelMixin
):# 查出当天的--》没过滤当前用户queryset = Collection.objects.all().filter(create_time__gte=datetime.now().date())serializer_class = CollectionSerializerdef get_serializer_class(self):if self.action == "create":return CollectionSaveSerializerelse:return CollectionSerializerdef list(self, request, *args, **kwargs):# 过滤当前用户采集的--》多种类型用户,数据权限不一样res = super().list(request, *args, **kwargs)today_count = len(self.get_queryset())return Response({"code": 100, "msg": "成功", "result": res.data, "today_count": today_count})## 删除人脸,没有删除 具体的图片--》定时任务--》每天晚上备份用户头像def destroy(self, request, *args, **kwargs):from libs.baidu_ai import BaiDuFaceinstance = self.get_object()# 百度ai中删除baidu = BaiDuFace()res = baidu.delete(instance.name_pinyin, instance.face_token)print(res)self.perform_destroy(instance)return Response()from .models import Area
from .serializers import AreaSerializer# 获取所有网格--》应该获取当前用户所管理的网格
class AreaView(GenericViewSet, ListModelMixin):queryset = Area.objects.all()serializer_class = AreaSerializer# 配置一个过滤类--》在过滤类中对 queryset进行过滤---》过滤出当前用户的网格#### 采集统计
from django.db.models import Count
from django.db.models.functions import Trunc
from .models import Collection
from .serializers import StatisticsListSerializerclass StatisticsView(GenericViewSet, ListModelMixin):# 统计时按天分组---》数个数queryset = (Collection.objects.annotate(date=Trunc("create_time", "day")).values("date").annotate(count=Count("id")).values("date", "count"))serializer_class = StatisticsListSerializer### 人脸检测视图类
class FaceView(GenericViewSet):def create(self, request, *args, **kwargs):avatar_object = request.data.get("avatar")if not avatar_object:return Response({"msg": "未提交图像", "code": 101})from libs.baidu_ai import BaiDuFaceai = BaiDuFace()result = ai.search(avatar_object)print(result)if result.get("error_code") == 0:  # 查询到# {'error_code': 0, 'error_msg': 'SUCCESS', 'log_id': 2159604393, 'timestamp': 1713864959, 'cached': 0, 'result': {'face_token': '095994eca64424cee347b59e0a7edc0e', 'user_list': [{'group_id': '100', 'user_id': 'li3si1xian4', 'user_info': '', 'score': 98.035797119141}]}}user = result.get("result").get("user_list")[0]user_info = user.get("user_info")user_id = user.get("user_id")score = user.get("score")return Response({"code": 100,"msg": "匹配成功","user_info": user_info,"user_id": user_id,"score": score,"avatar": "",})else:return Response({"code": 102, "msg": "匹配失败,该人员可能不是我社区人员,注意防范"})# 语音识别
class VoiceView(GenericViewSet):def create(self, request, *args, **kwargs):voice_object = request.data.get("voice")# with open('./a.wav','wb') as f:#     f.write(voice_object.read())ai = BaiDuVoice()result = ai.speed(voice_object)# {'corpus_no': '6847771638436561158', 'result': ['你是不是打过来?'], 'sn': '15921476781594371078', 'err_msg': 'success.', 'err_no': 0}if result.get("err_no") == 0:return Response({"code": 100, "msg": "识别成功", "result": result.get("result")})else:return Response({"code": 101, "msg": "识别失败"})class ActivityView(GenericViewSet, ListModelMixin):queryset = Activity.objects.all().order_by("date")serializer_class = ActivitySerializerclass NoticeView(GenericViewSet, ListModelMixin):queryset = Notice.objects.all().order_by("create_time")serializer_class = NoticeSerializerclass LoginView(GenericViewSet):@action(methods=["GET"], detail=False)def send_sms(self, request, *args, **kwargs):# 1 取出前端传入手机号mobile = request.query_params.get("mobile")# 2 获取随机验证码code = get_code()# 3 验证码放到缓存cache.set(f"sms_{mobile}", code)# 4 发送短信res = send_sms_by_phone(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 如果用户不存在,创建用户,再签发tokenfake = Faker("zh_CN")username = fake.name()user = UserInfo.objects.create(mobile=mobile, name=username)refresh = RefreshToken.for_user(user)return Response({"code": 100,"msg": "登录成功","token": str(refresh.access_token),"name": user.name,"score": user.score,"avatar": "http://127.0.0.1:8000/media/" + str(user.avatar),})else:return Response({"code": 101, "msg": "验证码错误"})@action(methods=["POST"], detail=False)def quick_login(self, request, *args, **kwargs):# 1 取出前端传入的codecode = 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://127.0.0.1:8000/media/" + str(user.avatar),})class ActivityJoinView(GenericViewSet):authentication_classes = [MyJSONWebTokenAuthentication]@action(methods=["POST"], detail=False)def join(self, request, *args, **kwargs):# 1 取出要参加的活动idactivity_id = request.data.get("id")# 2 取出当前登录用户user = request.user# 2 查到当前活动activity = Activity.objects.filter(pk=activity_id).first()# 3 判断时间,判断人数# 4 判断是否报名过join_record = JoinRecord.objects.filter(activity_id=activity_id, user=user).first()if join_record:return Response({"code": 101, "msg": "已经报名过,不用重复报名"})else:# 5 包名人数+1,报名报存入activity.count = activity.count + 1activity.save()JoinRecord.objects.create(activity=activity, user=user)# 6 返回报名成功return Response({"code": 100, "msg": "报名成功"})

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/721404.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MainWindows移动View文件夹和使用Window.DataContext单例绑定需要修改的地方

项目结构移动文件夹后需要修改的三个地方 1、App.xaml2、MainWindow.xaml3、MainWindow.xaml.cs单例绑定需要修改的地方 MainWindow.xaml

ClickHouse内幕(2)基础数据结构

ClickHouse以性能好被大家所熟知,而一个数据库的性能优化是一个庞大的系统性工程。本文着眼于ClickHouse内部的基础数据结构,以揭露ClickHouse性能优化的冰山一角。 在软件工程中并不是所有的执行路径都需要优化,只有关键执行路径才需要花费大力气进行优化。对于数据库领域来…

PyQT5之菜单栏和工具栏

from PyQt5 import QtWidgets from PyQt5 import QtCore, QtGui import sys import cv2class ButtonPanel(QtWidgets.QWidget):def __init__(self, *args, **kwargs):super().__init__(*args, **kwargs)select_btn = QtWidgets.QPushButton("图像选择")self.path_lab…

CH32系列MCU SysTick使用与计算

1、关于SysTick CH32F103/203: CH32F103/203为Cortex-M3内核,SysTick是一个24位的向下递减计数器,计数器每计数一次的时间可配置为1/时基。当SysTick重装载数寄存器的值递减到0的时候,产生一次中断。CH32F系列MCU SysTick由4个寄存器控制,具体如下图。具体介绍可参考《CM3…

在线安装 qt 下载安装慢以及安装报错无法下载存档 not found——解决方式

一、下载安装QT的在线下载器可以在 QT 官网下载开源的安装包(需要登陆) 或者在各大大学的镜像站中下,比如:mirrors.nju.edu.cn(可选)解压出下载的压缩包,拿到 qt-unified-windows-x64-online.exe/dmg/run 本体在终端中,输入 ./包名 --mirror https://mirror.nju.edu.…

【Linux驱动设备开发详解】11.内存与I/O访问

1.内存管理单元 高性能处理器一般会提供一个内存管理单元(MMU),用于辅助操作系统尽心修改内存管理,提供虚拟地址和物理地址的映射、内存访问权限保护和Cache缓存控制等硬件支持。 1.1MMU基本概念 1.1.1 概念含义 1.TLB(Translation Lookaside Buffer): 旁路转换缓存,TLB是MMU…

OOP第二次博客作业

一、前言 又做了三次PTA练习,前一次还是之前三次的迭代训练,后面两次又是一个新的模型。//终于是换模型了//题目类型都差不多,更注重类与类之间的联系,增加的内容就是对类的设计更复杂了,类的种类也更多了。但总体的逻辑不变。 二、分析第四次判题程序 (1)设计与分析 本…

HTTP Status 400 – Bad Request

1. 问题2. 原因org.apache.juli.logging.DirectJDKLog:log|Error parsing HTTP request headerNote: further occurrences of HTTP header parsing errors will be logged at DEBUG level.java.lang.IllegalArgumentException: Request header is too largeat org.apache.coyot…

使用jmeter,响应体response body中有两个同名的cookies时,如何获取第二个cookie进行跨线程组使用

如图两个同名cookie:.AspNetCore.Cookies正则表达式提取器 引用名称:loginCookie 正则表达式:Set-Cookie: (.AspNetCore.Cookies=.*?;) 模板:$1$(确保正确匹配到第二个 .AspNetCore.Cookies) 匹配数字2beanshell后置处理程序 ${__setProperty(loginCookie,${loginCookie…

IOS 手机 new Date 之后显示的是NaN-NaN-NaN

上周同事让我改一个入参,让用后端返回的时间作为入参,获取视频内容。我习惯成自然,利用了原来的时间格式化函数。函数里面有一个new Date()如下面截图: 部分IOS机型里面,2024-06-07里面的-他识别不出来,他会识别/,所以导致出现NaN-NaN-NaN。 这样的话,还是不要这样直接…

[中文参数] AGFA027R31C2I3V、AGFA027R31C2I3E、AGFA027R31C2E3E、AGFA027R31C2E4X面向互联世界的可编程逻辑产品

Agilex™ 7 F 系列采用Intel 10-nm SuperFin 工艺技术打造而成,提供高达 58 Gbps 的收发器速率、支持多种精度定点和浮点运算的高级 DSP 块,以及高性能的加密块。Agilex™ FPGA 产品组合包含一系列产品,可充分满足每一个技术领域(从边缘到嵌入式系统,再到通信和数据中心)…

美团面试:说说Netty的零拷贝技术?

零拷贝技术(Zero-Copy)是一个大家耳熟能详的技术名词了,它主要用于提升 IO(Input & Output)的传输性能。 那么问题来了,为什么零拷贝技术能提升 IO 性能? 1.零拷贝技术和性能 在传统的 IO 操作中,当我们需要读取并传输数据时,我们需要在用户态(用户空间)和内核态…