Python后端(Flask)+ 微信小程序
首先flask的session用不了,只能用全局变量来实现。
·
小程序结构说明:
/miniprogram
│── app.js # 小程序全局 JS 逻辑
│── app.json # 全局配置文件
│── app.wxss # 全局样式文件
│── pages/ # 小程序页面目录
│ ├── index/ # 首页
│ │ ├── index.js # 逻辑文件
│ │ ├── index.json # 页面配置文件
│ │ ├── index.wxml # 结构文件
│ │ ├── index.wxss # 样式文件
│ ├── mine/ # 个人中心页面
│── components/ # 组件目录
│── utils/ # 工具函数目录
│── sitemap.json # 站点配置
对于app.json的说明:
{
// 小程序的所有页面路径,数组的第一项为小程序的首页
"pages": [
"pages/index/index", // 首页
"pages/mine/mine" // 我的页面
],
// 全局窗口配置
"window": {
"navigationBarTitleText": "我的小程序", // 导航栏标题文本
"navigationBarBackgroundColor": "#ffffff", // 导航栏背景颜色(白色)
"navigationBarTextStyle": "black", // 导航栏文字颜色(黑色)
"backgroundColor": "#eeeeee", // 小程序页面的背景颜色(灰色)
"backgroundTextStyle": "dark" // 下拉时的背景文字颜色(dark 深色模式)
},
// 底部导航栏配置
"tabBar": {
"color": "#8a8a8a", // tabBar 未选中文字颜色
"selectedColor": "#ff0000", // tabBar 选中文字颜色(红色)
"backgroundColor": "#ffffff", // tabBar 背景颜色(白色)
"borderStyle": "black", // tabBar 上边框颜色(黑色)
"list": [ // tabBar 选项列表(至少 2 个,最多 5 个)
{
"pagePath": "pages/index/index", // tab 对应的页面路径
"text": "首页", // tab 显示的文字
"iconPath": "images/home.png", // 未选中时的图标路径(相对路径)
"selectedIconPath": "images/home_active.png" // 选中时的图标路径(相对路径)
},
{
"pagePath": "pages/mine/mine", // tab 对应的页面路径
"text": "我的", // tab 显示的文字
"iconPath": "images/mine.png", // 未选中时的图标路径
"selectedIconPath": "images/mine_active.png" // 选中时的图标路径
}
]
},
// 网络请求超时时间配置(单位:毫秒)
"networkTimeout": {
"request": 10000, // 发起 HTTPS 请求的超时时间(10秒)
"connectSocket": 10000, // WebSocket 连接超时时间(10秒)
"uploadFile": 10000, // 上传文件超时时间(10秒)
"downloadFile": 10000 // 下载文件超时时间(10秒)
},
// 是否开启调试模式
"debug": true // 开启后,开发者工具的 Console 中会显示调试信息
}
app.js
(全局逻辑)说明:
- 小程序启动 → 触发
onLaunch()
- 获取本地存储的用户信息(如果有)
- 进入前台 → 触发
onShow()
- 切换到后台 → 触发
onHide()
- 发生错误时 → 触发
onError(err)
App({
// 全局数据(所有页面都可以访问)
globalData: {
userInfo: null, // 存储用户信息(初始值为空)
apiUrl: "https://api.example.com" // 服务器 API 地址
},
// 小程序启动时触发(仅触发一次)
onLaunch() {
console.log("小程序启动"); // 在控制台输出 "小程序启动"
// 读取本地存储的用户信息(如果有)
wx.getStorage({
key: "userInfo", // 读取 key 为 "userInfo" 的本地存储数据
success: (res) => { // 读取成功
this.globalData.userInfo = res.data; // 将数据存入 globalData
}
});
},
// 小程序从后台切换到前台时触发
onShow() {
console.log("小程序显示"); // 在控制台输出 "小程序显示"
},
// 小程序从前台切换到后台时触发
onHide() {
console.log("小程序隐藏"); // 在控制台输出 "小程序隐藏"
},
// 小程序发生错误时触发(例如代码异常或 API 调用失败)
onError(err) {
console.error("小程序出错:", err); // 在控制台输出错误信息
}
});
Python代码
首先flask的session用不了,只能用全局变量来实现。
import pymysql
from flask import Flask, request, jsonify, session
from flask_cors import CORS
from flask import make_response
app = Flask(__name__)
CORS(app, supports_credentials=True) # 允许带上凭据(cookies)
app.secret_key = 'your_secret_key'
# 数据库配置
db_config = {
'host': 'localhost',
'user': 'root',
'password': '123456',
'database': 'pet',
'charset': 'utf8mb4'
}
current_user_id = None
@app.route('/login', methods=['POST'])
def login():
global current_user_id # 声明使用全局变量
data = request.get_json()
username = data.get('username')
password = data.get('password')
connection = pymysql.connect(**db_config)
try:
with connection.cursor() as cursor:
sql = "SELECT * FROM users WHERE username=%s AND password=%s"
cursor.execute(sql, (username, password))
result = cursor.fetchone()
if result:
current_user_id = result[0] # 设置全局变量
print(f"User ID set globally: {current_user_id}")
return jsonify({'message': '登录成功', 'status': 'success', 'data': {'id': current_user_id, 'username': result[1]}})
else:
return jsonify({'message': '登录失败', 'status': 'fail'}), 401
finally:
connection.close()
@app.route('/register', methods=['POST'])
def register():
data = request.get_json()
print("Received data:", data) # 打印接收到的数据
username = data.get('username')
password = data.get('password')
# 检查用户名和密码是否提供
if not username or not password:
return jsonify({'message': '用户名和密码不能为空', 'status': 'fail'}), 400
connection = pymysql.connect(**db_config)
try:
with connection.cursor() as cursor:
# 查询数据库以检查用户名是否已存在
sql_check = "SELECT * FROM users WHERE username=%s"
cursor.execute(sql_check, (username,))
result = cursor.fetchone()
if result:
return jsonify({'message': '用户名已存在', 'status': 'fail'}), 400
# 插入新用户
sql_insert = "INSERT INTO users (username, password) VALUES (%s, %s)"
cursor.execute(sql_insert, (username, password))
connection.commit()
return jsonify({'message': '注册成功', 'status': 'success'}), 201
except pymysql.MySQLError as db_err:
return jsonify({'message': '数据库错误', 'status': 'fail', 'error': str(db_err)}), 500
except Exception as e:
return jsonify({'message': '注册失败', 'status': 'fail', 'error': str(e)}), 500
finally:
connection.close() # 确保连接在完成后关闭
@app.route('/profile', methods=['GET'])
def profile():
global current_user_id # 声明使用全局变量
if current_user_id is None:
return jsonify({'message': '未登录', 'status': 'fail'}), 401
# 查询用户信息
connection = pymysql.connect(**db_config)
try:
with connection.cursor() as cursor:
sql = "SELECT username, nickname, phone, email FROM users WHERE id=%s"
cursor.execute(sql, (current_user_id,))
result = cursor.fetchone()
if result:
user_data = {
"username": result[0],
"nickname": result[1],
"phone": result[2],
"email": result[3]
}
return jsonify({'message': '获取成功', 'status': 'success', 'data': user_data})
else:
return jsonify({'message': '用户未找到', 'status': 'fail'}), 404
finally:
connection.close()
#==========================发布笔记===============================
@app.route('/post_note', methods=['POST'])
def post_note():
global current_user_id
if current_user_id is None:
return jsonify({'message': '未登录', 'status': 'fail'}), 401
data = request.get_json()
print(data)
content = data.get('content')
if not content:
return jsonify({'message': '笔记内容不能为空', 'status': 'fail'}), 400
connection = pymysql.connect(**db_config)
try:
with connection.cursor() as cursor:
sql_insert = "INSERT INTO notes (user_id, content) VALUES (%s, %s)"
cursor.execute(sql_insert, (current_user_id, content))
connection.commit()
return jsonify({'message': '发布成功', 'status': 'success'}), 201
except pymysql.MySQLError as db_err:
return jsonify({'message': '数据库错误', 'status': 'fail', 'error': str(db_err)}), 500
finally:
connection.close()
# ========================== 获取笔记和评论 ===========================
@app.route('/get_note/<int:note_id>', methods=['GET'])
def get_note(note_id):
connection = pymysql.connect(**db_config)
try:
with connection.cursor() as cursor:
# 获取笔记
sql_get_note = "SELECT content, created_at FROM notes WHERE id=%s"
cursor.execute(sql_get_note, (note_id,))
note = cursor.fetchone()
if not note:
return jsonify({'message': '笔记未找到', 'status': 'fail'}), 404
# 获取评论
sql_get_comments = "SELECT user_id, comment, created_at FROM comments WHERE note_id=%s"
cursor.execute(sql_get_comments, (note_id,))
comments = cursor.fetchall()
return jsonify({
'message': '获取成功',
'status': 'success',
'data': {
'content': note[0],
'comments': [
{'user_id': comment[0], 'comment': comment[1], 'created_at': comment[2]}
for comment in comments
]
}
})
except pymysql.MySQLError as db_err:
return jsonify({'message': '数据库错误', 'status': 'fail', 'error': str(db_err)}), 500
finally:
connection.close()
# ========================== 提交评论 ================================
@app.route('/post_comment', methods=['POST'])
def post_comment():
data = request.get_json()
note_id = data.get('note_id')
comment = data.get('comment')
user_id = data.get('user_id')
if not note_id or not comment or not user_id:
return jsonify({'message': '笔记ID、评论内容和用户ID不能为空', 'status': 'fail'}), 400
connection = pymysql.connect(**db_config)
try:
with connection.cursor() as cursor:
# 检查笔记是否存在
sql_check_note = "SELECT id FROM notes WHERE id=%s"
cursor.execute(sql_check_note, (note_id,))
note_exists = cursor.fetchone()
if not note_exists:
return jsonify({'message': '笔记不存在', 'status': 'fail'}), 404
# 插入评论
sql_insert_comment = "INSERT INTO comments (note_id, user_id, comment) VALUES (%s, %s, %s)"
cursor.execute(sql_insert_comment, (note_id, user_id, comment))
connection.commit()
return jsonify({'message': '评论成功', 'status': 'success'}), 201
except pymysql.MySQLError as db_err:
return jsonify({'message': '数据库错误', 'status': 'fail', 'error': str(db_err)}), 500
finally:
connection.close()
#========================all_notes====展示全部笔记====================
@app.route('/get_notes', methods=['GET'])
def get_notes():
connection = pymysql.connect(**db_config)
try:
with connection.cursor() as cursor:
sql = "SELECT id, content, user_id FROM notes" # 假设你有一个 'notes' 表存储笔记和用户ID
cursor.execute(sql)
notes = cursor.fetchall()
notes_list = [{'id': note[0], 'content': note[1], 'user_id': note[2]} for note in notes]
return jsonify({'message': '获取成功', 'status': 'success', 'data': {'notes': notes_list}})
except pymysql.MySQLError as db_err:
return jsonify({'message': '数据库错误', 'status': 'fail', 'error': str(db_err)}), 500
finally:
connection.close()
if __name__ == '__main__':
app.run()
微信小程序代码
Login
Page({
data: {
username: '',
password: ''
},
onUsernameInput: function(e) {
this.setData({
username: e.detail.value
});
},
onPasswordInput: function(e) {
this.setData({
password: e.detail.value
});
},
login: function() {
const { username, password } = this.data;
if (!username || !password) {
wx.showToast({
title: '请输入账号和密码',
icon: 'none'
});
return;
}
wx.request({
url: 'http://127.0.0.1:5000/login',
method: 'POST',
data: {
username,
password
},
success: function(res) {
if (res.statusCode === 200) {
const data = res.data;
wx.showToast({
title: data.message,
icon: data.status === 'success' ? 'success' : 'none'
});
if (data.status === 'success') {
// 登录成功后,处理返回的数据
const userData = data.data; // 获取数组数据
console.log(userData);
wx.redirectTo({
url: '/pages/index/index'
});// 这里可以根据需要进行进一步处理
// 可以在这里进行页面跳转等操作
}
} else {
wx.showToast({
title: '登录失败',
icon: 'none'
});
}
},
fail: function(err) {
wx.showToast({
title: '网络错误',
icon: 'none'
});
console.error(err);
}
});
},
goToRegister: function() {
wx.redirectTo({
url: '/pages/register/register' // 修改为目标页面的路径
});
}
});
<view class="container">
<view class="input-group">
<input type="text" placeholder="请输入用户名" bindinput="onUsernameInput" />
</view>
<view class="input-group">
<input type="password" placeholder="请输入密码" bindinput="onPasswordInput" />
</view>
<button bindtap="login">登录</button>
<button bindtap="goToRegister">注册</button> <!-- 添加注册按钮 -->
</view>
/* 样式文件 */
.container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #f5f5f5;
padding: 20px;
}
.input-group {
width: 100%;
max-width: 300px;
margin-bottom: 20px;
}
input {
width: 100%;
padding: 10px;
border: 1px solid #ccc;
border-radius: 4px;
font-size: 16px;
}
button {
width: 100%;
max-width: 300px;
padding: 10px;
background-color: #007bff;
color: white;
border: none;
border-radius: 4px;
font-size: 16px;
cursor: pointer;
}
button:hover {
background-color: #0056b3;
}
profile
Page({
data: {
username: '',
nickname: '',
phone: '',
email: ''
},
goToIndex() {
wx.navigateTo({
url: '/pages/index/index', // 笔记页面的路径
});
},
onLoad: function() {
wx.request({
url: 'http://127.0.0.1:5000/profile',
method: 'GET',
success: (res) => {
if (res.statusCode === 200) {
const data = res.data.data;
this.setData({
username: data.username,
nickname: data.nickname,
phone: data.phone,
email: data.email
});
} else {
wx.showToast({
title: res.data.message,
icon: 'none'
});
}
},
fail: (err) => {
wx.showToast({
title: '网络错误',
icon: 'none'
});
console.error(err);
}
});
}
});
<view class="container">
<view class="info-section">
<view class="info-item">
<text>用户名:</text>
<text>{{username}}</text>
</view>
<view class="info-item">
<text>昵称:</text>
<text>{{nickname}}</text>
</view>
<view class="info-item">
<text>电话:</text>
<text>{{phone}}</text>
</view>
<view class="info-item">
<text>邮箱:</text>
<text>{{email}}</text>
</view>
</view>
<!-- 前往笔记页面的按钮 -->
<view class="button-section">
<button bindtap="goToIndex">返回主页</button>
</view>
</view>
.container {
padding: 20px;
background-color: #f8f8f8; /* 背景颜色 */
border-radius: 8px; /* 圆角 */
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); /* 阴影效果 */
}
.info-section {
margin-bottom: 20px; /* 下边距 */
}
.info-item {
margin-bottom: 15px; /* 每项的下边距 */
padding: 10px; /* 内边距 */
background-color: #ffffff; /* 每项的背景颜色 */
border: 1px solid #e0e0e0; /* 边框颜色 */
border-radius: 5px; /* 边框圆角 */
display: flex; /* 使用flex布局 */
justify-content: space-between; /* 子项两端对齐 */
align-items: center; /* 垂直居中对齐 */
}
.info-item text {
color: #333333; /* 文本颜色 */
font-size: 16px; /* 字体大小 */
}
button {
background-color: #007aff; /* 按钮背景颜色 */
color: white; /* 按钮文本颜色 */
padding: 10px 15px; /* 按钮内边距 */
border: none; /* 无边框 */
border-radius: 5px; /* 圆角 */
font-size: 16px; /* 字体大小 */
cursor: pointer; /* 鼠标悬停时的光标样式 */
}
button:hover {
background-color: #005bb5; /* 悬停时的背景颜色 */
}
register
Page({
data: {
username: '',
password: ''
},
onUsernameInput: function(e) {
this.setData({
username: e.detail.value
});
},
onPasswordInput: function(e) {
this.setData({
password: e.detail.value
});
},
register: function() {
const { username, password } = this.data;
if (!username || !password) {
wx.showToast({
title: '请输入账号和密码',
icon: 'none'
});
return;
}
wx.request({
url: 'http://127.0.0.1:5000/register',
method: 'POST',
data: {
username,
password
},
success: function(res) {
if (res.statusCode === 200) {
const data = res.data;
wx.showToast({
title: data.message,
icon: data.status === 'success' ? 'success' : 'none'
});
} else {
wx.showToast({
title: '注册失败',
icon: 'none'
});
}
},
fail: function(err) {
wx.showToast({
title: '网络错误',
icon: 'none'
});
console.error(err);
}
});
}
});
<view class="container">
<view class="input-group">
<input type="text" placeholder="请输入用户名" bindinput="onUsernameInput" />
</view>
<view class="input-group">
<input type="password" placeholder="请输入密码" bindinput="onPasswordInput" />
</view>
<button bindtap="register">注册</button>
</view>
/* 样式文件 */
.container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #f5f5f5;
padding: 20px;
}
.input-group {
width: 100%;
max-width: 300px;
margin-bottom: 20px;
}
input {
width: 100%;
padding: 10px;
border: 1px solid #ccc;
border-radius: 4px;
font-size: 16px;
}
button {
width: 100%;
max-width: 300px;
padding: 10px;
background-color: #007bff;
color: white;
border: none;
border-radius: 4px;
font-size: 16px;
cursor: pointer;
}
button:hover {
background-color: #0056b3;
}
index
Page({
// 跳转到发布笔记页面
goToPublishNote() {
wx.navigateTo({
url: '/pages/notes/notes', // 发布笔记页面的路径
});
},
// 跳转到查看全部笔记页面
goToAllNotes() {
wx.navigateTo({
url: '/pages/all_notes/all_notes', // 全部笔记页面的路径
});
},
goToProfile() {
wx.navigateTo({
url: '/pages/profile/profile', // 全部笔记页面的路径
});
}
});
<view class="container">
<button bindtap="goToPublishNote">发布笔记</button>
<button bindtap="goToAllNotes">查看全部笔记</button>
<button bindtap="goToProfile">进入个人页面</button>
</view>
all_notes
Page({
data: {
notes: [], // 笔记列表
},
// 页面加载时获取所有笔记
onLoad() {
this.fetchNotes();
},
// 获取笔记列表
fetchNotes() {
wx.request({
url: 'http://127.0.0.1:5000/get_notes', // 获取笔记的后端接口
method: 'GET',
success: (res) => {
if (res.data.status === 'success') {
this.setData({ notes: res.data.data.notes });
} else {
wx.showToast({
title: res.data.message,
icon: 'none',
});
}
},
fail: () => {
wx.showToast({
title: '请求失败',
icon: 'none',
});
},
});
},
// 选择某个笔记时触发
selectNote(event) {
const noteId = event.currentTarget.dataset['noteId'];
const userId = event.currentTarget.dataset['userId'];
// 跳转到笔记详情页面,并传递noteId和userId作为参数
wx.navigateTo({
url: `/pages/note_detail/note_detail?noteId=${noteId}&userId=${userId}`,
});
},
});
<view class="note-list">
<block wx:for="{{notes}}" wx:key="id">
<view class="note-item" bindtap="selectNote" data-note-id="{{item.id}}" data-user-id="{{item.user_id}}">
<text>笔记内容: {{item.content}}</text>
<text>用户ID: {{item.user_id}}</text>
</view>
</block>
</view>
notes
Page({
data: {
noteContent: '', // 发布的笔记内容
commentContent: '', // 评论的内容
notes: [], // 笔记列表
selectedNoteId: null, // 选中的笔记ID
comments: [] // 当前笔记的评论列表
},
// 输入笔记内容
onInputNote(event) {
this.setData({
noteContent: event.detail.value
});
},
// 发布笔记
postNote() {
const { noteContent } = this.data;
if (!noteContent) {
wx.showToast({
title: '笔记内容不能为空',
icon: 'none'
});
return;
}
wx.request({
url: 'http://127.0.0.1:5000/post_note',
method: 'POST',
data: {
content: noteContent
},
success: (res) => {
if (res.data.status === 'success') {
wx.showToast({ title: '发布成功' });
this.fetchNotes(); // 重新获取笔记列表
this.setData({ noteContent: '' });
} else {
wx.showToast({ title: res.data.message, icon: 'none' });
}
}
});
},
});
<view class="container">
<!-- 发布笔记区域 -->
<view class="post-note">
<textarea placeholder="请输入笔记内容" bindinput="onInputNote" value="{{noteContent}}"></textarea>
<button bindtap="postNote">发布笔记</button>
</view>
</view>
.container {
padding: 20px;
}
.post-note textarea, .post-note button {
margin-bottom: 10px;
width: 100%;
}
.note-list {
margin-top: 20px;
}
.note-item {
padding: 10px;
background-color: #f5f5f5;
margin-bottom: 10px;
border-radius: 5px;
}
.comment-list {
margin-top: 20px;
}
.comment-item {
padding: 5px;
background-color: #eee;
margin-bottom: 5px;
border-radius: 3px;
}
note_detail
Page({
data: {
noteId: null,
userId: null,
noteContent: '',
comments: [], // 评论列表
newComment: '', // 用户输入的新评论
},
onLoad(options) {
const { noteId, userId } = options;
this.setData({ noteId, userId });
this.fetchNoteDetail(noteId);
this.fetchComments(noteId);
},
// 根据noteId获取笔记详情
fetchNoteDetail(noteId) {
wx.request({
url: `http://127.0.0.1:5000/get_note/${noteId}`,
method: 'GET',
success: (res) => {
if (res.data.status === 'success') {
this.setData({ noteContent: res.data.data.content });
} else {
wx.showToast({
title: res.data.message,
icon: 'none',
});
}
},
fail: () => {
wx.showToast({
title: '请求失败',
icon: 'none',
});
},
});
},
// 获取该笔记的评论
fetchComments(noteId) {
wx.request({
url: `http://127.0.0.1:5000/get_comments/${noteId}`, // 获取评论的接口
method: 'GET',
success: (res) => {
if (res.data.status === 'success') {
this.setData({ comments: res.data.data.comments });
} else {
wx.showToast({
title: res.data.message,
icon: 'none',
});
}
},
fail: () => {
wx.showToast({
title: '请求失败',
icon: 'none',
});
},
});
},
// 处理评论输入
handleCommentInput(event) {
this.setData({ newComment: event.detail.value });
},
// 提交评论
submitComment() {
if (!this.data.newComment.trim()) {
wx.showToast({
title: '请输入评论内容',
icon: 'none',
});
return;
}
wx.request({
url: 'http://127.0.0.1:5000/post_comment',
method: 'POST',
data: {
note_id: this.data.noteId,
comment: this.data.newComment,
user_id: this.data.userId, // 假设使用userId代表发表评论的用户
},
success: (res) => {
if (res.data.status === 'success') {
wx.showToast({
title: '评论成功',
});
// 评论成功后,重新获取评论列表
this.fetchComments(this.data.noteId);
this.setData({ newComment: '' }); // 清空输入框
} else {
wx.showToast({
title: res.data.message,
icon: 'none',
});
}
},
fail: () => {
wx.showToast({
title: '评论失败',
icon: 'none',
});
},
});
},
});
<view class="note-detail">
<text>笔记ID: {{noteId}}</text>
<text>用户ID: {{userId}}</text>
<text>笔记内容: {{noteContent}}</text>
<!-- 评论部分 -->
<view class="comments-section">
<text>评论列表:</text>
<block wx:for="{{comments}}" wx:key="id">
<view class="comment-item">
<text>用户{{item.user_id}}: {{item.comment}}</text>
</view>
</block>
</view>
<!-- 新增评论输入框 -->
<view class="add-comment">
<input type="text" placeholder="输入你的评论" value="{{newComment}}" bindinput="handleCommentInput" />
<button bindtap="submitComment">提交评论</button>
</view>
</view>
更多推荐
所有评论(0)