基於微信小程序的個人記事本(含后端程序及配置信息)
博客班級 | https://edu.cnblogs.com/campus/zjcsxy/SE2020 |
---|---|
作業要求 | https://edu.cnblogs.com/campus/zjcsxy/SE2020/homework/11334 |
作業目標 | 1. 編寫一個小程序,可以全新編寫,也可以學習別人的小程序進行修改 2. 熟悉git代碼管理流程,將源代碼上傳到到github 3. 在博客園班級中寫一篇相應的博文 |
作業源代碼 | https://github.com/VinceMockghy/notes_wechat_mini_program |
學號 | 31801201 |
院系 | 浙大城市學院計算機系 |
姓名班級 | 計算1803 郭浩源 |
前言
第一次軟件工程的作業,我的想法是不僅僅是前端。做這樣一個小程序的初衷是這樣的,雖然現在市面上有很多很多跨平台的備忘錄程序(基本上都是windows和android或者ios跨平台,然而我用linux),而且那些程序基本上都是別人的服務器,想象一下自己的備忘錄被記錄在別人服務器的數據庫中,總感覺很奇怪。我就趁着這個小程序的機會,寫完整的后端程序搭載在個人服務器並綁定個人域名,結合小程序做前端,制作一整套的記事本小程序。后續的話在我的linux上隨便寫個bash腳本從服務器拉內容就可以全平台共通了。
效果展示
下面這個gif演示了這個小程序的一些使用方法。
首先點擊加號可以跳轉到添加筆記的界面,點擊保存可以將筆記內容保存到服務器的數據庫中
保存成功后自動返回主界面,剛才寫的筆記就會出現在界面上(當天寫的筆記會被做出標記筆記上半部分會顏色深一點)
點開剛才創建的筆記,可以修改筆記中的內容,並同步到服務器的數據庫,也可以將這個筆記刪除。
git 提交歷史
微信小程序界面
頁面分配
"/page/index/index" 小程序的主界面,記事本的每條記錄都會出現在這里
"/page/create/index" 記事本創建記錄的界面,可以在這里添加編寫標題和內容,添加記錄
"/page/edit/index" 記事本中單條記錄的編輯界面,可以修改記錄的內容並保存(刪除記錄也放在這里)
index主界面
/page/index/index.wxml
在第一個<view class = "col">
中,創建了添加筆記的那個方框以及中間的加號圖片,點擊這個地方會調用onNewItem,跳轉到/page/create/index頁面進行新建筆記
和上一部分同級的是一個<block>
標簽,在這個標簽中,通過js文件中數據,以notekey為鍵值進行遍歷。遍歷得到的是每一次做過的筆記,點擊后會觸發onEditItem,跳轉到/page/edit/index頁面。
<!--index.wxml-->
<view class="container">
<view class="col">
<view class="item addBox" bindtap="onNewItem">
<image class="addIcon" src="../../image/add.png"></image>
</view>
</view>
<block wx:for="{{items}}" wx:key="{{notekey}}">
<view class="col">
<view class='item note {{item.quality}}' data-key="{{item.countkey}}" bindtap="onEditItem">
<view class='content'>
<view class='txt'>{{item.title}}</view>
</view>
<view class='bottom'>
<view class='txt'>
{{item.year}}-{{item.month}}-{{item.day}}
</view>
</view>
</view>
</view>
</block>
</view>
/page/index/index.js
在這個js中主要有下面幾點需要說明一下
API文件后面會介紹
首先最開始通過require導入的myserveAPI文件是根據后台文件寫的發送GET和POST請求用的API文件。因為這是主界面,在頁面加載的時候就需要獲取到數據庫中的全部筆記,所以修改了onLoad方法。在onLoad中調用了serve API中的getnote方法從服務器的數據庫中獲取所有筆記信息。這一部分主要是做了個時間戳的轉換,在主界面上可以看到年月日,同時當天的筆記會被單獨標記(根據這里時間的判斷修改quality的值來控制單獨標記)。
onNewItem方法就是簡單的將頁面跳轉到/page/create/index上,而onEditItem卻有些不同,他需要傳入參數,參數的內容就是當前點擊到的這篇筆記信息,將選中筆記的信息轉為json格式作為參數傳入,就能在修改筆記后通過筆記信息中的notekey來修改對應的內容了。
這里還修改了onShow方法,在onShow的時候調用onLoad,做到重載的功能。主要是因為當進入筆記編輯狀態保存后返回主界面或者是創建筆記成功並返回主界面后,並不會重新加載這個界面,也就是數據寫入數據庫了,但本地還不知道,也無法驗證是否真的寫入了數據庫。所以這里通過這個方法,在主界面顯示的時候都會調用一次,從服務器的數據庫重新獲取數據,並重新顯示
// pages/create/index.js
var serve = require('../../utils/myserveAPI')
Page({
data: {
items: [],
},
onLoad: function (options) {
let that = this;
serve.getnote((data) => {
var len = Object.keys(data).length
var today = new Date()
var thisYear = today.getFullYear()
var thisMonth = (today.getMonth() + 1 < 10 ? '0' + (today.getMonth() + 1) : today.getMonth() + 1);
var thisDay = (today.getDate() < 10 ? '0' + (today.getDate()) : today.getDate());
for (let i = 0; i < len; i++) {
var opdate = new Date(data[i]["updatetime"])
data[i]["year"] = opdate.getFullYear();
data[i]["month"] = (opdate.getMonth() + 1 < 10 ? '0' + (opdate.getMonth() + 1) : opdate.getMonth() + 1);
data[i]["day"] = (opdate.getDate() < 10 ? '0' + (opdate.getDate()) : opdate.getDate());
data[i]["countkey"] = i
if (data[i]["year"] === thisYear && data[i]["month"] === thisMonth && data[i]["day"] === thisDay) {
data[i]["quality"] = "today"
} else {
data[i]["quality"] = "none"
}
}
console.log(data)
this.setData({
items: data
})
})
},
onShow: function () {
this.onLoad()
},
onNewItem: function (event) {
wx.navigateTo({
url: "/pages/create/index"
})
},
onEditItem: function (event) {
console.log(event.currentTarget.dataset.key)
var json = this.data.items[event.currentTarget.dataset.key]
console.log(json)
wx.navigateTo({
url: "/pages/edit/index?json=" + JSON.stringify(json)
})
}
})
create創建筆記界面
/page/create/index.wxml
創建筆記界面沒什么好多說的,就是在外框放了一個form表單,里面兩個輸入框,通過最后的保存按鈕觸發表單的提交。
<!--pages/create/index.wxml-->
<form bindsubmit="onSubmit">
<view class="container">
<view class='title'><input name="title" placeholder-class="placeholder" placeholder="在此輸入標題" value="" /></view>
<view class='row' catchtap="onFocus">
<textarea class='text' maxlength="5000" auto-height="True" placeholder-class="placeholder" name="content" focus="{{focus}}" auto-focus="true" value="" placeholder="點擊添加文本" />
</view>
<view class='bottom'>
<button formType="submit" class='btn success'>保存</button>
</view>
</view>
</form>
/page/create/index.js
這里最開始還是一樣調用serveAPI接口,在保存按鈕被點擊並調用onSubmit方法后觸發API接口的addnote進行筆記的添加。
// pages/create/index.js
var serve = require('../../utils/myserveAPI')
Page({
data: {
item: {
"title": "",
"content": "",
}
},
onSubmit: function (event) {
var item = this.data.item;
item.title = event.detail.value.title;
item.content = event.detail.value.content;
this.setData({
item: item
});
console.log(this.data.item)
serve.addnote((data) => {
if (data === "done") {
wx.showToast({
title: "保存成功",
success: function () {
// 返回首頁
setTimeout(function () {
wx.hideToast();
wx.navigateBack();
}, 1000)
}
})
}
}, this.data.item)
}
})
edit修改界面
/page/edit/index.wxml
這一部分和上面的新建界面一樣,只是多了個刪除按鈕
<!--pages/edit/index.wxml-->
<form bindsubmit="onSubmit">
<view class="container">
<view class='title'><input name="title" placeholder-class="placeholder" placeholder="在此輸入標題" value="{{item.title}}" /></view>
<view class='row' catchtap="onFocus">
<textarea class='text' maxlength="5000" auto-height="True" placeholder-class="placeholder" name="content" focus="{{focus}}" auto-focus="true" value="{{item.content}}" placeholder="點擊添加文本" />
</view>
<view class='bottom'>
<button formType="submit" class='btn success'>保存</button>
<button class='btn delete' bindtap="onDelete">刪除</button>
</view>
</view>
</form>
/page/edit/index.js
在主界面的分析中有說過在進入編輯界面的時候會傳入選中筆記的信息,所以在這個界面加載的時候,我們需要將傳入的json信息解析出來,並保存到變量中。
onSubmit和onDelete兩個方法分別調用serveAPI中的editnote編輯筆記和delnote刪除筆記,並在成功后返回主界面
// pages/edit/index.js
var serve = require('../../utils/myserveAPI')
Page({
data: {
item: {
"title": "",
"content": "",
}
},
onLoad: function (options) {
var that = this;
var json = JSON.parse(options.json)
that.setData({
item: json
})
},
onSubmit: function (event) {
var item = this.data.item;
item.title = event.detail.value.title;
item.content = event.detail.value.content;
this.setData({
item: item
});
serve.editnote((data) => {
if (data === "done") {
wx.showToast({
title: "保存成功",
success: function () {
// 返回首頁
setTimeout(function () {
wx.hideToast();
wx.navigateBack();
}, 1000)
}
})
}
}, this.data.item)
},
onDelete: function () {
serve.delnote((data) => {
if (data === "done") {
wx.showToast({
title: "保存成功",
success: function () {
// 返回首頁
setTimeout(function () {
wx.hideToast();
wx.navigateBack();
}, 1000)
}
})
}
}, this.data.item.notekey)
}
})
serveAPI文件
/utils/myserveAPI.js
這個文件是為了調用服務器上的后端程序而寫的API接口。
首先最開始的四個變量分別對應增刪改查這四個方法的URL
然后這四個方法中只有查可以直接通過GET的方法獲取到,其他的都需要發送數據包進行POST請求(后端是這么定的,這邊也就這樣寫)
GET請求沒什么好說的,也沒什么坑,但POST請求中一定要有header(沒有的話就會報500 Internal server error,當時我還以為是服務器被我搞炸了...)。
然后因為這些請求完成后都需要一些數據處理,這里就需要傳入回調函數,具體傳入的回調函數內容可以回上面調用這些方法的地方去看,就不一一贅述,畢竟沒啥難度。
最后面呢就是對這個API的封裝(看教程寫的,不這樣寫調用不了方法,和封python的包差不多)
var GETNOTE = "https://api.zghy.xyz/getnote"
var ADDNOTE = "https://api.zghy.xyz/addnote"
var DELNOTE = "https://api.zghy.xyz/delnote"
var EDITNOTE = "https://api.zghy.xyz/editnote"
function getnote(callback) {
wx.request({
url: GETNOTE,
data: {},
method: 'GET',
success: function (res) {
callback(res.data)
}
})
}
function addnote(callback, notedata) {
wx.request({
url: ADDNOTE,
data: notedata,
method: 'POST',
header: {
"Content-Type": "application/x-www-form-urlencoded" //用於post
},
success: function (res) {
callback(res.data)
}
})
}
function editnote(callback, notedata) {
var json = {
"notekey": notedata["notekey"],
"title": notedata["title"],
"content": notedata["content"]
}
console.log(json)
wx.request({
url: EDITNOTE,
data: json,
method: 'POST',
header: {
"Content-Type": "application/x-www-form-urlencoded" //用於post
},
success: function (res) {
console.log(res.data)
callback(res.data)
}
})
}
function delnote(callback, notedata) {
var json = {"notekey": notedata}
wx.request({
url: DELNOTE,
data: json,
method: 'POST',
header: {
"Content-Type": "application/x-www-form-urlencoded" //用於post
},
success: function (res) {
callback(res.data)
}
})
}
module.exports = {
getnote: getnote,
addnote: addnote,
delnote: delnote,
editnote: editnote
}
前端微信小程序界面上的程序大致就這么些,雖然頁面少了點,但我覺得作為小程序的初學足夠了,剩下的能玩點后端的以及后端與前端建立連接這種還是不錯的。
后端程序
后端采用python flask建立web微框架,通過小程序中的request向個人域名發送GET、POST請求,將筆記的標題內容在服務器的數據庫中進行增刪改查。
web服務結構
'/addnote' 添加筆記 需要發送POST請求
'/getnote' 查詢筆記 直接發送GET請求
'/delnote' 刪除筆記 需要發送POST請求
'/editnote' 修改筆記 需要發送POST請求
代碼結構
這里使用python flask庫創建四個web接口,每個接口都有對應的http請求方式。
請求成功如果有數據獲取就獲取發送過來的數據,沒有就直接連接mysql服務器,寫寫SQL語句,進行簡單的增刪改查(有點數據庫短學期的味道了,不過python的數據庫操作總覺得比java方便點,也有可能是錯覺)
服務接口寫完后就可以通過服務器ip地址+8080端口進行訪問。至於為什么是8080端口,有段曲折的經歷,后面說
# -*- coding: utf-8 -*-
"""
@Author : guohaoyuan
@Time : 2020/10/15 下午6:53
"""
from flask import Flask, request
import pymysql
import mysql_info as SQLINFO
import time
import datetime
app = Flask(__name__)
# 添加筆記 需要發送POST請求
@app.route('/addnote', methods=['POST'])
def addnote():
now = datetime.datetime.now()
now = now.strftime("%Y-%m-%d %H:%M:%S")
title = request.form['title']
content = request.form['content']
conn = pymysql.connect(SQLINFO.HOST, SQLINFO.USER, SQLINFO.PASSWORD, SQLINFO.DATABASE)
cursor = conn.cursor()
sql = "INSERT INTO NOTE(title,content,createtime,updatetime) VALUES ('%s','%s','%s','%s')" % (
title, content, now, now)
try:
cursor.execute(sql)
conn.commit()
except:
conn.rollback()
conn.close()
return "done"
# 查詢筆記 直接發送GET請求
@app.route('/getnote', methods=['GET'])
def getnote():
conn = pymysql.connect(SQLINFO.HOST, SQLINFO.USER, SQLINFO.PASSWORD, SQLINFO.DATABASE)
cursor = conn.cursor()
sql = '''SELECT * FROM NOTE'''
results = {}
try:
cursor.execute(sql)
notes = cursor.fetchall()
count = 0
for row in notes:
results.update(NOTE(row[0], row[1], row[2], row[3], row[4]).formateNote(count))
count += 1
except:
print("Unable to fetch data!")
conn.close()
return results
# 刪除筆記 需要發送POST請求
@app.route('/delnote', methods=['POST'])
def delnote():
notekey = request.form['notekey']
conn = pymysql.connect(SQLINFO.HOST, SQLINFO.USER, SQLINFO.PASSWORD, SQLINFO.DATABASE)
cursor = conn.cursor()
sql = "DELETE FROM NOTE WHERE notekey = %d" % int(notekey)
try:
cursor.execute(sql)
conn.commit()
except:
conn.rollback()
conn.close()
return "done"
# 修改筆記 需要發送POST請求
@app.route('/editnote', methods=['POST'])
def editnote():
now = datetime.datetime.now()
now = now.strftime("%Y-%m-%d %H:%M:%S")
notekey = request.form['notekey']
title = request.form['title']
content = request.form['content']
conn = pymysql.connect(SQLINFO.HOST, SQLINFO.USER, SQLINFO.PASSWORD, SQLINFO.DATABASE)
cursor = conn.cursor()
sql = "UPDATE NOTE SET title = '%s',content = '%s',updatetime = '%s' WHERE notekey = %d" % (
title, content, now, int(notekey))
try:
cursor.execute(sql)
conn.commit()
except:
conn.rollback()
conn.close()
return "done"
class NOTE:
def __init__(self, notekey, title, content, createtime, updatetime):
self.notekey = int(notekey)
self.title = str(title)
self.content = str(content)
self.createtime = createtime.strftime("%Y-%m-%d %H:%M:%S")
self.updatetime = updatetime.strftime("%Y-%m-%d %H:%M:%S")
def formateNote(self, count):
result = {count: {"notekey": self.notekey, "title": self.title, "content": self.content,
"createtime": self.createtime, "updatetime": self.updatetime}}
return result
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8080, debug=False)
服務器配置
首先你需要租一個服務器,然后需要買一個域名,然后你的域名需要ICP備案,然后你還要學習一些服務器上的web端口跳轉以及域名解析與跳轉等等的知識。
微信小程序中的服務器需求
這是微信小程序官方給出的可以被微信小程序中wx.request成功發出HTTPS請求的服務器配置,注意是HTTPS,所以一定要有域名(還好我有不然就白搞了)
然后這里還提示如果需要配置端口只能是8080端口,但我最開始不是這個端口,因為我是直接域名跳轉,不需要輸入端口,但是它還是提示我當前的網址不符合規定,所以才改成的8080端口。然而改了之后還是說不符合規定,所以最后也沒管他。
nginx服務器配置域名反向代理
在我的服務器上之前因為個人博客的緣故配置了nginx,一開始開啟flask后端服務后,所有走api.zghy.xyz這個二級域名的信息全都會指向默認的443端口,所以就需要配置相應的反向端口代理。
這里就給個反向代理配置文件吧
這里需要個HTTPS的認證證書,阿里雲服務器那里有免費的證書可以買,給二級域名買一個就行了
server {
listen 443 ssl; # https 監視端口是 443
server_name api.zghy.xyz;
# 下面兩個是認證HTTPS證書后下載的證書地址,我這里就不放出來了
ssl_certificate "";
ssl_certificate_key "";
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 5m;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
location / {
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://47.102.101.77:8080;#這里配置了端口的反向代理
}
location ~ \.php$ {
try_files $uri =404;
fastcgi_pass unix:/run/php-fpm/www.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
server{
listen 80;
server_name api.zghy.xyz;
rewrite ^(.*)$ https://$host$1 permanent;
}
Python Flask程序后台運行配置
那么服務器上我們的python程序應該如何一直運行呢?這里我用了pm2這個東西,他能將前台程序放到后台運行,並保持運行狀態。
效果就是下面這樣,通過這個list可以看到小程序的api后台一直在運行。