文章目录
3-1 Request&Response对象
3-2 Django的RESTful URL设计
3-3 实现个人助手功能清单发布
apis\views\menu.py 管理app
在app.js中定义全局接口地址
定义传递的应用数据
// pages/menu/menu.js
const app = getApp()
Page({
/**
* 页面的初始数据
*/
data: {
grids: [{
"name": "应用1"
}, {
"name": "应用2"
}], // 九宫格内容
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function(options) {
this.updateMenuData()
},
/**
* 请求后台,更新menu数据
*/
updateMenuData: function() {
var that = this
wx.request({
url: app.globalData.serverUrl + app.globalData.apiVersion + '/service/menu',
success: function(res) {
var menuData = res.data.data
console.log(menuData)
that.setData({
grids: menuData
})
}
})
},
页面渲染数据
3-4 实现图文消息
这里是采用本地存储
3-5 Django类视图
import json
from django.views import View
from django.http import HttpResponse, JsonResponse
from utils.response import CommonResponseMixin
from thirdparty import juhe
class WeatherView(View, CommonResponseMixin):
def get(self, request):
pass
def post(self, request):
data = []
received_body = request.body.decode('utf-8')
received_body = json.loads(received_body)
print(received_body)
cities = received_body.get('cities')
for city in cities:
result = juhe.weather(city.get('city'))
result['city_info'] = city
data.append(result)
response_data = self.wrap_json_response(data)
return JsonResponse(data=response_data, safe=False)
class CommonResponseMixin(object):
@classmethod
def wrap_json_response(cls, data=None, code=None, message=None):
response = {}
if not code:
code = ReturnCode.SUCCESS
if not message:
message = ReturnCode.message(code)
if data is not None:
response['data'] = data
response['result_code'] = code
response['message'] = message
return response
3-6 Django图文消息应用
#!/usr/bin/python
import os
import hashlib
from django.views import View
from django.http import Http404, HttpResponse, FileResponse, JsonResponse
from backend import settings
from utils.response import ReturnCode, CommonResponseMixin
class ImageListView(View, CommonResponseMixin):
def get(self, request):
image_files = os.listdir(settings.IMAGES_DIR)
response_data = []
for image_file in image_files:
response_data.append({
'name': image_file,
'md5': image_file[:-4]
})
response = self.wrap_json_response(data=response_data)
return JsonResponse(data=response, safe=False)
class ImageView(View, CommonResponseMixin):
def get(self, request):
md5 = request.GET.get('md5')
imgfile = os.path.join(settings.IMAGES_DIR, md5 + '.jpg')
print(imgfile)
if os.path.exists(imgfile):
data = open(imgfile, 'rb').read()
# return HttpResponse(data, content_type='image/jpg')
return FileResponse(open(imgfile, 'rb'), content_type='image/jpg')
else:
response = self.wrap_json_response(code=ReturnCode.RESOURCE_NOT_FOUND)
return JsonResponse(data=response, safe=False)
def post(self, request):
files = request.FILES
response_data = []
for key, uploaded_file in files.items():
print(key)
print(uploaded_file)
content = uploaded_file.read()
md5 = hashlib.md5(content).hexdigest()
path = os.path.join(settings.IMAGES_DIR, md5 + '.jpg')
print(md5)
with open(path, 'wb+') as f:
f.write(content)
response_data.append({
'name': key,
'md5': md5
})
response = self.wrap_json_response(data=response_data, code=ReturnCode.SUCCESS)
return JsonResponse(data=response, safe=False)
def delete(self, request):
md5 = request.GET.get('md5')
img_name = md5 + '.jpg'
path = os.path.join(settings.IMAGES_DIR, img_name)
if os.path.exists(path):
os.remove(path)
message = 'remove success.'
else:
message = 'file(%s) not found.' % img_name
response = self.wrap_json_response(code=ReturnCode.SUCCESS, message=message)
return JsonResponse(data=response, safe=False)
3-8 综合实践之生活服务-上
pages/weather.js
// pages/weather/weather.js
const app = getApp()
const popularCities = [
// {
// "province": "上海市",
// "city": "上海",
// "area": "徐汇区"
// },
{
"province": "河南省",
"city": "郑州",
"area": "回族区"
}
]
Page({
/**
* 页面的初始数据
*/
data: {
isAuthorized: false,
weatherData: null
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function(options) {
this.updateWeatherData()
},
updateWeatherData: function() {
wx.showLoading({
title: '加载中',
})
var that = this;
wx.request({
url: app.globalData.serverUrl + app.globalData.apiVersion + '/service/weather',
method: 'POST',
header: {
'content-type': 'application/json' // 默认值
},
// 查询的城市
data: {
cities: popularCities
},
success: function(res){
console.log(res.data.data)
that.setData({
weatherData: res.data.data
})
wx.hideLoading()
}
})
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function() {
this.updateWeatherData()
}
})
Django后端在上面
3-8 综合实践之生活服务-下
主要上图片备份,小程序前端back.js逻辑
const app = getApp()
const imageUrl = app.globalData.serverUrl + app.globalData.apiVersion + '/service/image'
Page({
data: {
// 需要上传的图片
needUploadFiles: [],
// backupedFiles每个元素四个字段 name, md5, path, isDownloaded
// 已下载的备份图片
downloadedBackupedFiles: []
},
// 选择图片上传
chooseImage: function(e) {
var that = this;
wx.chooseImage({
sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
success: function(res) {
// 返回选定照片的本地文件路径列表,tempFilePath可以作为img标签的src属性显示图片
that.setData({
needUploadFiles: that.data.needUploadFiles.concat(res.tempFilePaths)
});
}
})
},
// 长按确认函数
longTapConfirm: function(e) {
var that = this
// 上传视图的确认菜单
var uploadConfirmList = ['取消上传']
// 已备份图片列表视图的确认菜单
var downloadedConfirmList = ['保存本地', '删除备份']
if (e.currentTarget.dataset.type == 'UploadView') {
var itemList = uploadConfirmList
} else {
var itemList = downloadedConfirmList
}
// 显示菜单
wx.showActionSheet({
itemList: itemList,
success: function(res) {
if (res.cancel) {
return
}
// 上传视图的确认菜单逻辑
if (e.currentTarget.dataset.type == 'UploadView' && res.tapIndex == 0){
var imgId = e.currentTarget.dataset.id
var newNeedUploadFiles = that.data.needUploadFiles
for (var i = 0; i < newNeedUploadFiles.length; i ++){
if(newNeedUploadFiles[i] == imgId){
newNeedUploadFiles.splice(i, 1)
}
that.setData({
needUploadFiles: newNeedUploadFiles
})
}
}
// 已备份图片列表视图的确认菜单逻辑
if (e.currentTarget.dataset.type == 'DownloadedView' && res.tapIndex == 1){
var imgIndex = e.currentTarget.dataset.index
var imgItem = that.data.downloadedBackupedFiles[imgIndex]
console.log(imgIndex)
console.log(imgItem)
console.log(that.data.downloadedBackupedFiles)
var newDownloadedBackupedFiles = that.data.downloadedBackupedFiles
newDownloadedBackupedFiles.splice(imgIndex, 1)
console.log(newDownloadedBackupedFiles)
that.setData({
// 删除图片本地
downloadedBackupedFiles: newDownloadedBackupedFiles
})
// 删除图片远端
that.deleteBackup(imgItem)
}
}
});
},
// 上传图片文件
uploadFiles: function() {
var that = this
that.setData({
newBackupedFiles: []
})
for (var i = 0; i < this.data.needUploadFiles.length; i++) {
var file = this.data.needUploadFiles[i]
wx.uploadFile({
url: app.globalData.serverUrl + app.globalData.apiVersion + '/service/image',
filePath: file,
name: 'test',
success: function(res) {
var resutData = JSON.parse(res.data)
var imgData = resutData.data[0]
var uploadedFile = {
'name': imgData.name,
'md5': imgData.md5,
'path': '',
'isDownloaded': false
}
// 上传成功的保存到newBackupedFiles数组里
that.downloadFile(uploadedFile)
}
})
}
wx.showToast({
title: '上传成功',
})
// 清空等待上传的文件列表
this.setData({
needUploadFiles: []
})
},
// 删除图片
deleteBackup: function(imgItem){
console.log('delete a backup file.' + imgItem)
wx.request({
url: imageUrl + '?md5=' + imgItem.md5,
method: 'DELETE',
success: function(res){
console.log(res)
wx.showToast({
title: '删除成功',
})
}
})
},
onLoad: function(){
this.downloadAllFromRemote()
},
// 下载所有的已备份图片
downloadAllFromRemote: function () {
var that = this
// 1. 请求后台获取已备份的图片列表
wx.request({
url: imageUrl + '/list',
method: 'GET',
success: function (res) {
var imageList = res.data.data
for (var i = 0; i < imageList.length; i++) {
// 2. 逐个调用downloadFile进行图片下载
that.downloadFile(imageList[i])
}
}
})
},
// 下载图片
downloadFile: function (imgItem) {
var that = this
var downloadUrl = imageUrl + '?md5=' + imgItem.md5
wx.downloadFile({
url: downloadUrl,
success: function (res) {
var filepath = res.tempFilePath
console.log(filepath)
var newDownloadedBackupedFiles = that.data.downloadedBackupedFiles
imgItem.path = filepath
newDownloadedBackupedFiles.unshift(imgItem)
that.setData({
downloadedBackupedFiles: newDownloadedBackupedFiles
})
console.log(newDownloadedBackupedFiles)
}
})
},
});
3-9 小程序股票、星座等功能实现
实现技术
实现技术:后端主要是利用聚合数据api接口,来查询出相应APP应用API所获取的数据返回给小程序前端,具体的示例也可参考上面获取 天气的例子
def stock(market, code):
'''
沪深股票
:param market: 上交所 = sh, 深交所 = sz
:param code: 股票编号
:return:
'''
key = ''
api = 'http://web.juhe.cn:8080/finance/stock/hs'
params = 'gid=%s&key=%s' % (market + code, key)
url = api + '?' + params
print(url)
response = requests.get(url=url, proxies=proxy.proxy())
data = json.loads(response.text)
data = data.get('result')[0].get('data')
response = {
'name': data.get('name'),
'now_price': data.get('nowPri'),
'today_min': data.get('todayMin'),
'today_max': data.get('todayMax'),
'start_price': data.get('todayStartPri'),
'date': data.get('date'),
'time': data.get('time')
}
response['is_rising'] = data.get('nowPri') > data.get('todayStartPri')
sub = abs(float(data.get('nowPri')) - float(data.get('todayStartPri'))) # 差值
response['sub'] = float('%.3f' % sub)
return response
"""
stock api
{
'resultcode': '200',
'reason': 'SUCCESSED!',
'result': [{
'data': {
'buyFive': '123700',
'buyFivePri': '33.780',
'buyFour': '41262',
'buyFourPri': '33.790',
'buyOne': '11747',
'buyOnePri': '33.820',
'buyThree': '134600',
'buyThreePri': '33.800',
'buyTwo': '92600',
'buyTwoPri': '33.810',
'competitivePri': '33.820',
'date': '2020-05-29',
'gid': 'sh600036',
'increPer': '-1.17',
'increase': '-0.400',
'name': '招商银行',
'nowPri': '33.820',
'reservePri': '33.850',
'sellFive': '13100',
'sellFivePri': '33.890',
'sellFour': '95400',
'sellFourPri': '33.880',
'sellOne': '1500',
'sellOnePri': '33.850',
'sellThree': '1000',
'sellThreePri': '33.870',
'sellTwo': '1200',
'sellTwoPri': '33.860',
'time': '15:00:00',
'todayMax': '34.130',
'todayMin': '33.700',
'todayStartPri': '33.860',
'traAmount': '1260905545.000',
'traNumber': '372374',
'yestodEndPri': '34.220'
},
'dapandata': {
'dot': '33.820',
'name': '招商银行',
'nowPic': '-0.400',
'rate': '-1.17',
'traAmount': '126091',
'traNumber': '372374'
},
'gopicture': {
'minurl': 'http://image.sinajs.cn/newchart/min/n/sh600036.gif',
'dayurl': 'http://image.sinajs.cn/newchart/daily/n/sh600036.gif',
'weekurl': 'http://image.sinajs.cn/newchart/weekly/n/sh600036.gif',
'monthurl': 'http://image.sinajs.cn/newchart/monthly/n/sh600036.gif'
}
}],
'error_code': 0
}
获取小程序应用名字
在app.yml中定义小程序的名字,注册路由返回给小程序前端
published:
- app:
category: life
application: weather
name: 天气
publish_date: 2018-10-01
url: /service/weather
desc: this is a weather app.
- app:
category: life
application: backup-image
name: 图片备份
publish_date: 2018-10-02
url: /service/image
desc: this is a backup image app.
- app:
category: life
application: stock
name: 股票
publish_date: 2018-10-03
url: /service/stock
desc: this is a stock app.
- app:
category: life
application: constellation
name: 星座运势
publish_date: 2018-10-03
url: /service/constellation
desc: this is a constellation app.
- app:
category: life
application: joke
name: 笑话
publish_date: 2018-10-03
url: /service/joke
desc: this is a joke app.
apis\views\menu.py
import os
import yaml
from django.http import JsonResponse
from backend import settings
import utils.response
def init_app_data():
data_file = os.path.join(settings.BASE_DIR, 'app.yaml')
with open(data_file, 'r', encoding='utf-8') as f:
apps = yaml.load(f)
return apps
def get_menu(request):
global_app_data = init_app_data()
published_apps = global_app_data['published']
# return JsonResponse(data=published_apps, safe=False, status=200)
response = utils.response.wrap_json_response(data=published_apps)
return JsonResponse(data=response, safe=False)
总结
backup是定义后端逻辑,如上传图等
server是定义各个app获取接口数据,传递给前端等操作,前端共用一个页面,通过判断应用类型来显示api的数据
小程序中menu是定义app应用页面,如获取后端传递的app名称,跳转app页面等操作
-
在前段app.json中定义整体的配置
{ "pages": [ "pages/index/index", "pages/menu/menu", "pages/backup/backup", "pages/weather/weather", "pages/stock/stock", "pages/service/service" ], "window": { "backgroundTextStyle": "light", "navigationBarBackgroundColor": "#fff", "navigationBarTitleText": "WeChat", "navigationBarTextStyle": "black" }, "tabBar": { "list": [ { "pagePath": "pages/index/index", "text": "首页", "iconPath": "", "selectedIconPath": "" }, { "pagePath": "pages/menu/menu", "text": "应用", "iconPath": "", "selectedIconPath": "" } ] }, "sitemapLocation": "sitemap.json" }
// pages/menu/menu.js
const app = getApp()
Page({
/**
* 页面的初始数据
*/
data: {
grids: [{
"name": "应用1"
}, {
"name": "应用2"
}], // 九宫格内容
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function(options) {
this.updateMenuData()
},
/**
* 请求后台,更新menu数据
*/
updateMenuData: function() {
var that = this
wx.request({
url: app.globalData.serverUrl + app.globalData.apiVersion + '/service/menu',
success: function(res) {
var menuData = res.data.data
console.log(menuData)
that.setData({
grids: menuData
})
}
})
},
onNavigatorTap: function(e) {
var index = e.currentTarget.dataset.index
var item = this.data.grids[index]
console.log(item)
if (item.app.application == 'weather') {
console.log('-------------')
wx.navigateTo({
url: '../weather/weather',
})
} else if (item.app.application == 'backup-image') {
wx.navigateTo({
url: '../backup/backup',
})
} else if (item.app.application == 'stock'){
wx.navigateTo({
url: '../stock/stock',
})
} else if (item.app.application == 'constellation'){
wx.navigateTo({
url: '../service/service?type=constellation',
})
} else if (item.app.application == 'joke'){
wx.navigateTo({
url: '../service/service?type=joke',
})
}
}
})