提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
最近需要工作写个小程序,这里记录一下期间学习的框架,以及遇到的各种问题,也供大家参考。
一、小程序框架
小程序自身分为两个主要部分独立运行:view 模块和 service 模块。在开发者工具中,它们独立运行于不同的 webivew tag 中。
view 模块负责前端界面显示,它由 wxml 和 wxss 转换后代码以及微信提供相关辅助模块组成。 一个 view 模块对应一个 页面, 小程序支持同时多个 view 存在。
service 模块负责后台逻辑,它由 js 代码以及微信提供的相关辅助模块组成。 一个应用只有一个 service 进程,它同样也是一个页面。它在程序生命周期内后台运行,service 模块通过与 view 模块实现不同但接口格式一样的微信JSBridge 对象跟后台通信。
具体内容详见下文,这里不再展开。:
https://blog.csdn.net/liyanfang1004/article/details/103735429?utm_medium=distribute.wap_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.wap_blog_relevant_pic&depth_1-utm_source=distribute.wap_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.wap_blog_relevant_pic
二、开发过程
1.工具准备
小程序需要使用微信开发者工具来进行开发,同时最常用的参考文档是微信小程序官方文档。在开发者工具中包含了模拟器、调试器等,内容很全面。也可以用云模式来在腾讯云进行后台开发。使得开发者不用搭建数据库等,专注于逻辑设计,简化后台设计
另外为了更好的展示小程序可以用一些工具插件,例如wx-charts和echarts等,这两个都可用于图形化展示统计数据。
2.开发程序
小程序一般包括主程序、pages、componets、images、style等文件夹。如下图所示,app.js、app,wxss等是主程序。pages文件夹内都是页面程序,其中默认第一个页面是index。如果涉及到云开发还包含cloudfunction文件夹,不过云功能主要是写后台API。
我这里举的例子需求是做一个支持在线随堂考试的小程序,能够收集学生的答案自动判分,并给老师呈现学生的成绩情况。
页面设计主要包含2个内容:1个是提供给学生的界面,主要用于在线测试;第二个则给老师,用于显示学生的成绩情况。在线测试的页面放在index下,给老师的页面放在managerSatis下,下面拣重点介绍一下。
inde文件夹下包含index.wxml, index.js, index.json, index.wxss。其中index.wxml描述了界面,index.js是页面逻辑程序,index.wxss定义了页面内容的格式, index.json可以包含使用的插件情况。以下是程序主要代码部分:
index.xml:
<view class="page-body">
<view class="page-section">
<view class="title_postion">
<view class="title_view"> 随堂测试题</view>
</view>
</view>
<view class="page-section">
<view class="weui-cells_title">
<text class="page-section-title-im">*</text>
<text class="cls3-a"> 请填写姓名</text>
</view>
<view class="weui-cell weui-cell_input">
<input class="label" auto-focus bindinput="bindNameInput" placeholder="请填写姓名"/>
</view>
</view>
.....
<view class="page-section page-section-gap">
<text>\n</text>
<text>\n</text>
<view class="page-section-title">
<text class="page-section-title-im">*</text>
<text class="cls3-a">1 农药残留不包括以下哪种?</text>
</view>
<radio-group bindchange="items1RadioChange">
<view class="label" wx:for="{{items1}}">
<radio value="{{item.value}}" ></radio>
<label class="label-1-text" ><text>{{item.name}}</text></label>
</view>
</radio-group>
</view>
....
<view class="button-sp-area">
<button class="btn1" wx:if = '{{notSubmit}}' bindtap="submit"> 提交 </button>
</view>
<view class="button-manager">
<button class="btn1" wx:if = '{{authority}}' bindtap="managerSwitch"> 查询结果 </button>
</view>
这里主要包含了单选、文本填写和按钮。其中单选主要用radio组件,多选可以用checkbox组件。
index.js
在这里插入代码片Page({
data: {
avatarUrl: './user-unlogin.png',
userInfo: {},
logged: false,
authority: false,
takeSession: false,
notSubmit: true,
requestResult: '',
result:0,
name: '',
num: '',
items1: [
{name: '农药原体', value: 'A'},
{name: '有毒代谢物', value: 'B',checked: 'true'},
{name: '防腐剂', value: 'C'},
{name: '降解产物', value: 'D'}
],
....
},
onLoad: function() {
this.authentication();
},
items1RadioChange(e) {
console.log('radio发生change事件,携带value值为:', e.detail.value)
const items = this.data.items1
this.answer_data.items1 = e.detail.value
for (let i = 0, len = items.length; i < len; ++i) {
items[i].checked = items[i].value === e.detail.value
if(items[i].checked === true)
{
this.answer_data.items1Name = items[i].name
}
}
this.setData({
items1:items
})
},
...
authentication: function () {
wx.cloud.callFunction({
name: 'login',
complete: res => {
console.log('res值为:', res.result)
const db = wx.cloud.database()
db.collection('author').get().then(res2=>{
console.log('res2值为:', res2.data);
if (res.result.openid === res2.data[0].openid) {
this.setData({
authority: true
})
}
})
}
})
},
bindNameInput: function (e) {
this.data.name = e.detail.value
},
...
submit(e)
{
if (this.answer_data.items1 === this.result_date.items1){
this.data.result += 10
}
....
const db = wx.cloud.database()
db.collection('test').add({
data: {
num: this.data.num,
name: this.data.name,
result: this.data.result
},
success(res) {
wx.showModal({
content: '已提交成功,禁止再提交。',
})
},
fail(res) {
wx.showToast({
icon: 'none',
title: '新增记录失败'
})
console.error('[数据库] [新增记录] 失败:', err)
}
})
this.setData({
notSubmit: false
})
},
managerSwitch(e)
{
wx.redirectTo({
url: '../managerSatis/managerSatis',
})
}
这里需要注意以下方面: 1 可以通过this.setData可以动态改变页面的数据。 2 authentication部分代码需要在云数据库端新建author数据库,并将管理者openid保存到author数据库中,这样管理者登录时可以通过openid的对比,可以改变authority值,在wxml中采用 wx:if = '{{authority}}',因此只有管理者才可以看到最后的统计结果。注意,这里提交了默认云函数的login函数,才能实现该功能。
关于index.wxss就不详细展开了,感兴趣的读者可以参见所提供的代码。
managerSatis部分主要是统计学生分数情况,采用了echars插件实现动态图像显示,以下是主要部分:
- 显示不同分数段的学生百分比;
- 显示数据库中每个学生的情况
managerSatis.xml:
<!--miniprogram/pages/managerSatis/managerSatis.wxml-->
<view class="page-body">
<view style='position:relative;width:100%;height:600rpx;'>
<ec-canvas id="mychartOne" canvas-id="mychart-bar" ec="{{ecOne}}"></ec-canvas>
</view>
<text class="a" > \n </text>
<view class='a' wx:for="{{cloudData}}">
<view >序号{{index+1}}:{{item.name}} \t {{item.num}} \t {{item.result}}</view>
<text class="a" > \n </text>
</view>
<view class="button-manager">
<button class="btn1" bindtap="updateResult"> 更新结果 </button>
</view>
</view>
managerSatis.js:
import * as echarts from '../../components/ec-canvas/echarts';
var Chart = null;
Page({
/**
* 页面的初始数据
*/
data: {
num100: 0,
num90To100:0,
num80To90:0,
num70To80:0,
num60To70:0,
num60:0,
cloudData:[],
ec:{
lazyLoad:true
},
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
const db = wx.cloud.database();
const _ = db.command
db.collection('test').where({
result: _.lt(60)
}).count({
//如果查询成功的话
success: res => {
console.log("信息查询:",res.total)
this.setData({
num60: res.total
})
console.log("信息查询6011111111111:",this.data.num60)
}
})
....
wx.cloud.callFunction({
// 云函数名称
name: 'sendData',
// 传给云函数的参数
success: res => {
console.log(res) // 3
this.setData({
cloudData: res.result.data
})
},
fail: console.error
})
this.ecComponentOne = this.selectComponent('#mychartOne');
this.init_echarts();
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function () {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage: function () {
},
init_echarts:function(){
this.ecComponentOne.init((canvas, width, height) =>{
Chart = echarts.init(canvas, null, {
width: width,
height: height
});
Chart.setOption(this.getOption());
Chart.on('click', function(handler, context){
});
return Chart;
});
},
getOption: function () {
var that=this;
console.log("进入了getOption方法中!")
var option = {
backgroundColor: "#ffffff",
color: ["#da8982", "#ec9683","#45B580","#b6e4c7","#B9C753","#dbce8e"],
series: [{
label: {
normal: {
show: true,
formatter: ' {b} \n {c} ',//({ d } %) b=名称 c=数值 d=百分比 \n换行
fontSize: 12,
fontWeight:700,
}
},
labelLine: { show: true },
type: 'pie',//饼图实例
center: ['50%', '55%'], //圆饼图的位置
radius: [0, '60%'], //圆饼图大小的比例
data: [{
value: this.data.num60,
name: '60分以下',
}
.....
],
itemStyle: {
emphasis: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 2, 2, 0.3)'
}
}
}]
};
return option;
},
updateResult(){
const db = wx.cloud.database();
const _ = db.command
db.collection('test').where({
result: _.lt(60)
}).count({
//如果查询成功的话
success: res => {
console.log("信息查询:",res.total)
this.setData({
num60: res.total
})
}
})
......
Chart.setOption(this.getOption());
Chart.on('click', function(handler, context){
});
}
})
该部分以下功能需要注意:
- 采用调用sendData的方式,接收云数据库中所有的感兴建的数据。如果仅在小程序前端采用查询函数只能推送10条所有消息。
- 采用echarts统计不同分数段学生数,主要通过Chart.setOption(this.getOption());这句来动态改变图形的取值。echarts可以实现动态的画图数据更新,wx-charts插件只能实现静态画图。
其中云函数sendData的代码如下:
managerSatis.js:
// 云函数入口文件
const cloud = require('wx-server-sdk')
cloud.init()
const db = cloud.database()
const test = db.collection('test')
// 云函数入口函数
exports.main = async (event, context) => {
const wxContext = cloud.getWXContext()
const MAX_LIMIT = 100
const countResult = await test.count()
const total = countResult.total
// 计算需分几次取
const batchTimes = Math.ceil(total / 100)
// 承载所有读操作的 promise 的数组
const tasks = []
for (let i = 0; i < batchTimes; i++) {
const promise = test.skip(i * MAX_LIMIT).limit(MAX_LIMIT).get()
tasks.push(promise)
}
// 等待所有
return (await Promise.all(tasks)).reduce((acc, cur) => ({
data: acc.data.concat(cur.data),
errMsg: acc.errMsg,
}))
}
该函数主要实现了将数据库中数据发送给小程序前端的功能。
具体所有的代码详见示例程序, 该代码能够成功部署并使用。
总结
腾讯的小程序系统还是对开发者非常友好的,能够支持快速实现各种应用。在开发过程中包括了体验版和正式版,其中正式版需要经过审查确认。