第5章豆豆云助教课程模块开发 对自己的不满足,是任何真正有天才的人的根本特征之一。 ——契诃夫 李嘉诚的一生就是永不满足的一生。13岁的李嘉诚辍学就业,一开始在玩具制造公司当推销员,由于工作出色,不到20岁便担任了这家公司的总经理。但是他并没有满足于现状,两年后,他用自己的积蓄以及筹来的7000美元 创办了自己的塑胶厂,就是后来的“长江塑胶厂”。 经营有道加上市场需求大,李嘉诚成了“塑胶花大王”,“长江塑胶厂”也成为世界上最大的塑料花生产厂。然而他依旧不满足,由于眼光独到,不久后开始涉足房地产,在1979年斥资6.2亿元,从汇丰集团购入22.4%的股权。他在1986年又大举进军加拿大,购入赫斯基能源公司超过半数的权益。经过40多年的打拼,李嘉诚的业务经营已经向全球发展, 他也成为中国商业界的传奇。 课程内容即将过半,希望大家能向李嘉诚学习,学习他那种永不满足现状的精神,戒骄戒躁,继续努力,向更高的目标进发。 本章主要完成课程模块的开发。在之前的tabBar中除了“我的”外,还有一栏是“主页面”, 这就是本章开发的课程模块,主页面主要包括课程信息和课程练习两个模块。另外,本章案例照搬了一些豆豆云的后台,因此出现“课程”的概念,但对用户来说是没有“加入课程”这个过程的。简单来说,我们做的小程序,就相当于把豆豆云中的一个课程做成独立的一个小程序。 5.1申请课程号 由于照搬了豆豆云的后台,涉及“加入课程”的概念,开发者需要向后台申请一个课程,得到课程号,申请链接如下: http://zjgsujiaoxue.applinzi.com/index.php/Api/User/createCourse?appid=123&courseName=1028教学&questionSet=1012&creater=工商大佬 其中,appid代表开发者小程序的AppID,courseName代表要创建的课程的名称,开发者可自定义,questionSet代表预设的题目集(后续无法更改),creater代表创建者。 例如, 小程序的AppID是123,创建的课程名称是“一起来学近代史”,对应的题库是表51中的1001,即questionSet是1001,创建者是“工商大佬”,那么开发者需要访问以下链接进行课程号的申请: http://zjgsujiaoxue.applinzi.com/index.php/Api/User/createCourse?appid=123&courseName=一起来学近代史&questionSet=1001&creater=工商大佬 其中,对于questionSet的题目集,后台提供了8个题目集供开发者选择,具体详见表51。 表51题目集信息 题目集ID题目集名称题 目 数 量 1001近代史题库1287 1002浙江工商大学新生入学考试题库1276 1003计算机网络题库219 1008C语言二级模拟考试题库120 1009毛泽东思想概论题库1766 1010C语言训练题1395 1011马克思主义基本原理概论2059 1012思想道德修养与法律基础1561 注意: 题目集ID为1004、1005、1006、1007的题库已作废。 选择好需要申请的课程后,访问对应的课程申请链接,网页中会即刻返回课程号,如图51所示。 图51访问链接获取课程号 此时申请课程号所用的小程序AppID与该课程号已经绑定了,课程号可保存下来。所有访问该小程序的用户默认加入该课程。另外在config.js文件中加入变量courseId,以便在后面代码中的引用,如图52所示。 图52在config.js中加入变量courseId 5.2课程模块页面布局 本节所讲的课程模块的页面布局是豆豆云助教的简化版本,豆豆云助教首页如图53所示。本案例主页面主要包括课程信息和课程练习模块,如图54所示。相较于豆豆云助教的主页面,本案例不涉及切换课程,没有教师端,所以不需要在线签到与加入课程模块。 图53豆豆云助教首页 图54案例主页面 5.2.1课程信息模块页面布局 课程信息模块主要包括课程名称、课程创建者、加入课程的人数以及课程号,如图54所示,本案例课程名称为“C游记”,对应的是C语言题库。对于课程信息模块的页面布局,同样可以参考WeUI样式中表单→List→带图标、说明的列表项,如图55所示。 图55带图标、说明的列表项 找到对应的WeUI样式后,将该样式的对应代码复制、粘贴到自己的项目代码中。带图标、说明的列表项具体代码如下: 标题文字 说明文字 其中,image组件中src属性对应的图片资源地址改为课程对应的图片, 注意应将课程图片放置在images文件夹下,图片资源地址为课程图片的绝对地址。例如,本案例图片存放在images文件夹下,图片名称为course_head.png,那么图片资源路径为/images/course_head.png。另外,由于图片太小,需要调整style中width和height的值至80px。 课程信息模块的课程名称、创建者、加入人数以及课程号等信息用标题文字的样式即可,无须使用说明文字样式。另外,通过字体大小与字体颜色不同来使得课程名称更加吸引用户注意。课程信息模块布局如图56所示。 图56课程信息模块布局 其中,课程信息模块wxml代码如下: 课程名称 创建者: 加入人数: 课程号: 在上述代码中涉及一些新的知识点,本节对代码中涉及的知识点进行简单讲解。 1. class和style的区别 在wxml代码中经常会发现一个view属性中既有class="weuicell__hd" ,又有style="position: relative;marginright: 10px;"。虽然两者都可以实现对页面的修改,但还是存在区别的。 比如在myinfo页面添加一个button做测试,该button的wxml代码如下: 另外,在myinfo.wxss中添加test样式,test样式中主要是跟style中一样定义了字体颜色,该样式代码如下: .test{ color:red; } 该button在style中color属性值为蓝色,class调用的test样式中color属性值为红色,无论如何修改test样式中color属性的值,按钮字体颜色都是蓝色,如图57所示。在wxml中前端读取数据都是通过就近原则,所以style是直接在页面语句中进行编写,在程序执行时,style>class。 图57测试button的字体颜色 2. position属性 position属性规定元素的定位类型。这个属性定义建立元素布局所用的定位机制。任何元素都可以定位,不过绝对 定位元素或固定元素会生成一个块级框,而不论该元素本身是什么类型。相对定位元素会相对于它在正常流中的默认位置偏移。其中position属性的值详见表52。 表52position属性的值 值描述 absolute生成绝对定位的元素,相对于 static 定位以外的第一个父元素进行定位。元素的位置通过left、top、right以及bottom属性进行规定 fixed生成绝对定位的元素,相对于浏览器窗口进行定位。 元素的位置通过left、top、right以及bottom属性进行规定 relative生成相对定位的元素,相对于其正常位置进行定位。因此,"left:20"会向元素的left位置添加20像素 static默认值。没有定位,元素出现在正常的流中(忽略 top、bottom、left、right或者zindex 声明) inherit规定应该从父元素继承 position 属性的值 3. marginright属性 marginright属性设置元素的右外边距,允许使用负值。marginright 属性的值详见表53。 表53marginright属性的值 值描述 值描述 auto浏览器设置的右外边距%定义基于父对象总高度的百分比右外边距 length定义固定的右外边距。默认值是0inherit规定应该从父元素继承右外边距 修改课程练习模块image属性中marginright的值为10px和100px,页面效果如图58和图59所示。 4. display属性 display属性规定元素应该生成的框的类型。这个属性用于定义建立布局时元素生成的显示框类型。对于HTML等文档类型,如果使用display不慎则会很危险,因为可能违反HTML中已经定义的显示层次结构。对于XML,由于XML没有内置的这种层次结构,所有display都是绝对必要的。display属性的值详见表54。 表54display属性的值 值描述 none此元素不会显示 block此元素将显示为块级元素,此元素前后会带有换行符 inline默认。此元素会被显示为内联元素,元素前后没有换行符 续表 值描述 inlineblock行内块元素,CSS2.1新增的值 listitem此元素会作为列表显示 runin此元素会根据上下文作为块级元素或内联元素显示 table此元素会作为块级表格来显示(类似 ),表格前后带有换行符 inlinetable此元素会作为内联表格来显示(类似
),表格前后没有换行符 tablerowgroup此元素会作为一个或多个行的分组来显示(类似 ) tableheadergroup此元素会作为一个或多个行的分组来显示(类似 ) tablefootergroup此元素会作为一个或多个行的分组来显示(类似 ) tablerow此元素会作为一个表格行显示(类似 ) tablecolumngroup此元素会作为一个或多个列的分组来显示(类似 ) tablecolumn此元素会作为一个单元格列显示(类似 ) tablecell此元素会作为一个表格单元格显示(类似
) tablecaption此元素会作为一个表格标题显示(类似
) inherit规定应该从父元素继承display属性的值 图58marginright的值为10px的页面效果 图59marginright的值为100px的页面效果 5.2.2课程练习模块页面布局 课程练习模块主要包括顺序练习、随机练习、章节练习、专项练习、收藏以及答错,相对于前面使用WeUI样式布局,课程练习模块要教 的是如何使用现有的开源代码,经过修改后开发出自己的小程序,这可以大大减轻开发者的工作量。 其中,课程练习模块的布局主要参考了GitHub上一个驾校考题的小程序前端代码,该项目下载地址为https://github.com/HuBinAdd/calculateswiperList。下载驾校考题源代码后,导入项目,可以看到该小程序的首页如图510所示。 找到驾校考题首页对应的index页面,将驾校考题中对应的练习模块前端代码复制到自己的项目中,其中只保留专 项练习与章节练习,随机练习与顺序练习就不需要了,编译后发现页面效果并不能正常显示,如图511所示。这是因为代码中涉及的样式不是WeUI样式,而是驾校考题小程序开发者自己写的样式,因此需要将index.wxss中的样式复制到自己项目的index.wxss中,但是发现课程练习模块还是有点问题,如图512所示。 图510驾校考题小程序首页 图511课程练习模块页面异常显示 图512课程练习模块样式不全 导致课程练习模块显示与驾校考题主页面不一致的原因是该部分代码中涉及的样式colhg6和colhg3在index.wxss中没有,在驾校考题源代码中,该样式写在app.wxss中,将对应的colhg6和colhg3样式复制到index.wxss文件中,具体样式代码如下: .col-hg-6 { float: left; box-sizing: border-box; width: 50%; } .col-hg-3 { float: left; box-sizing: border-box; width: 25%; } 其中涉及了float、boxsizing和width三种属性。 1. float属性 float属性定义元素在哪个方向浮动。任何元素都可以浮动。浮动元素会生成一个块级框,而不论它本身是何种元素。如果浮动非替换元素,则要指定一个明确的宽度; 否则,它们会尽可能地窄。其中float属性的值详见表55。 表55float属性的值 值描述 left元素向左浮动 right元素向右浮动 none默认值。元素不浮动,并会显示其在文本中出现的位置 inherit规定应该从父元素继承 float 属性的值 2. boxsizing属性 boxsizing属性允许以特定的方式定义匹配某个区域的特定元素。例如,假如需要并排放置两个带边框的框,可通过将boxsizing设置为"borderbox"。这可令页面呈现出带有指定宽度和高度的框,并把边框和内边距放入框中。其中boxsizing属性的值详见表56。 表56boxsizing属性的值 值描述 contentbox宽度和高度分别应用到元素的内容框。在宽度和高度之外绘制元素的内边距和边框 borderbox为元素设定的宽度和高度决定了元素的边框。也就是说,为元素指定的任何内边距和边框都将在已设定的宽度和高度内进行绘制。通过从已设定的宽度和高度分别减去边框和内边距才能得到内容的宽度和高度 inherit规定应从父元素继承 boxsizing 属性的值 3. width属性 width属性设置元素的宽度。这个属性定义元素内容区的宽度,在内容区外面可以增加内边距、边框和外边距; 行内非替换元素会忽略这个属性。其中width属性的值详见表57。 表57width属性的值 值描述 auto默认值。浏览器可计算出实际的宽度 length使用 px、cm 等单位定义宽度 %定义基于包含块(父元素)宽度的百分比宽度 inherit规定应该从父元素继承 width 属性的值 添加colhg6和colhg3样式后,通过编译后发现专项练习与章节练习的布局只占了页面宽度的一半,使得课程练习模块布局不是很美观,如图513所示。这是由于专项练习和章节练习使用的是colhg3样式,该样式的width属性只有25%,两个元素共50%,因此只占了页面宽度的一半。要让专项练习和章节练习撑满整个页面,就要将colhg3改为colhg6。课程练习模块最终页面布局如图514所示。 另外,将模拟考试元素中文字“模拟考试”改为“顺序练习”,“最高成绩: 分”改为“做题数: 题”。最终课程练习模块wxml代码为: 课程练习 顺序练习 做题数: 题 图513专项练习与章节练习布局异常 图514课程练习模块页面最终布局 收藏 () 答错 () 专项练习 章节练习 5.3课程模块页面逻辑实现 与课程模块页面相关的逻辑主要包括请求加入课程与获取当前课程信息两个逻辑,由于照搬豆豆云助教的后台,所以会涉及 请求加入课程的逻辑,并且需要添加获取课程信息的逻辑,用于课程信息模块,显示用户所加入课程对应的课程信息。 5.3.1请求加入课程逻辑 在5.1节中成功申请课程号后,每个开发者都创建了一个自己的课程,本节主要内容就是如何让用户加入开发者创建的课程中。请求加入课程主要是用户在第一次进入小程序即发生的请求,为减少请求次数,将该逻辑写在app.js中,用户第一次使用时通过getAddedCourse接口向后台发送请求以确认该用户是否已加入课程。若用户已加入该课程,则返回该用户已加入的课程号,当success为false时表示未加入,执行加入课程逻辑。具体代码如下: wx.request({ url: userUrl +'getAddedCourse', data: { 'openid': wx.getStorageSync('jiaoxue_OPENID'), }, success:function(res) { if (!res.data.success) { wx.request({ url: userUrl +'addCourse', data: { openid: wx.getStorageSync('jiaoxue_OPENID'), courseId: courseId }, success:function(res) { if (res.success) { wx.setStorageSync('jiaoxue_courseList', courseId) } }, fail:function(res) { } }) }else { wx.setStorageSync('jiaoxue_addedCourse', res.data.msg) } } }) 5.3.2获取当前课程逻辑 成功加入课程后,用户首页需要显示所加入课程的信息。获取当前课程主要通过current接口向后台发送请求,获取课程信息后显示在前端,其中index.js中的代码如下: //index.js //获取应用实例 const app = getApp() const userUrl=require('../../config.js').userUrl const courseId = require('../../config.js').courseId Page({ data: { current_course:{}, }, onLoad:function () { this.getCurrentCourse(courseId) }, getCurrentCourse(course_id=''){ wx.request({ url:userUrl +'current', data:{ current_course_id: course_id, openid: wx.getStorageSync('jiaoxue_OPENID'), }, success: res=>{ console.log('res1',res) this.setData({ current_course: res.data.data }) } }) } }) index.js文件中主要在data数组中定义了一个current_course数组,然后写了一个getCurrentCourse()函数,函数中主要实现了请求名为current的API请求,向后台发送current_course_id和openid的值,请求成功后,将res.data.data赋值给current_course,使用console.log('res1',res)打印res的值,即可在Console面板中看到后台返回的课程信息,如图515所示。 图515返回的课程信息 获取课程信息后,需要将课程信息显示在首页中,因此还需要对index.wxml中课程信息模块的代码进行简单修改,其中将“课程名称”改为变量{{current_course['name']?current_course['name']:"未知"}},并在“创建者: ”“加入人数: ”和“课程号: ”后面分别加上变量{{current_course['teacher'] ['name']?current_course['teacher']['name']:"未知"}}、{{current_course['count']?current_course['count']:"未知"}}和{{current_course['id']?current_course['id']:"未知"}}。变量通过三目运算进行判断,当获取到当前课程信息时显示对应的课程信息,如图516所示,否则显示未知,如图517所示。 图516课程信息正常显示页面 图517课程信息未知页面 5.4作业思考 一、 讨论题 1. 如何在代码中宏定义课程号? 2. 关于课程信息模块中image组件的style样式,如果使用wxss文件要怎么实现? 3. 如何灵活地使用与border相关的属性? 4. 获取当前课程逻辑中不使用getCurrentCourse()方法,如何实现获取当前课程信息? 二、 单选题 1. 微信小程序向后台请求数据时关于method: 'POST'和method: 'GET',以下说法错误的是()。 A. GET用来从服务器上获得数据,而POST用来向服务器上传递数据 B. GET是不安全的,因为在传输过程中数据被放在请求的URL中 C. GET传输的数据量大,这主要是受URL长度限制; 而POST可以传输少量的数据,所以上传文件只能使用GET D. 使用POST传输的数据,可以通过设置编码的方式正确地转换为中文; 而GET传输的数据却没有变化 2. 以下关于CSS position属性值的说法错误的是()。 A. absolute生成绝对定位的元素,相对于 static 定位以外的第一个父元素进行定位 B. fixed生成绝对定位的元素,相对于浏览器窗口进行定位。元素的位置通过 left、top、right以及bottom属性进行规定 C. relative生成相对定位的元素,相对于其正常位置进行定位,因此,"left:20" 会向元素的 left位置添加20像素 D. static规定应该从父元素继承 position 属性的值 3. 以下关于 CSS marginright的属性值的说法错误的是()。 A. inherit规定应该从父元素继承左外边距 B. auto为浏览器设置的右外边距 C. length定义固定的右外边距,默认值是 0 D. %定义基于父对象总高度的百分比右外边距 4. 以下关于display属性的说法错误的是()。 A. display: inline为内联元素,只需要必要的宽度,不强制换行 B. display: block(块元素)是一个元素,占用了全部宽度,在前后都是换行符 C. display: none可以隐藏某个元素 D. display: hidden可以隐藏某个元素 5. 以下关于display: none和visibility: hidden的说法正确的是()。 A. visibility: hidden可以隐藏某个元素,但隐藏的元素仍需占用与未隐藏之前一样的空间 B. display: none可以隐藏某个元素,且隐藏的元素会占用空间。也就是说,该元素不但被隐藏了,而且该元素依然会在页面布局中存在 C. visibility: hidden可以隐藏某个元素,且隐藏的元素不会占用任何空间 D. display: none可以隐藏某个元素,但隐藏的元素仍需占用与未隐藏之前一样的空间 6. 以下关于class和style的说法正确的是()。 A. 在wxml当中前端读取数据都是通过就近原则,所以style是直接在页面语句中进行编写,在程序执行的时候,style>class B. class对应的样式优先级大于style C. class对应的样式响应先于style D. class对应的样式和style优先级相同 7. 以下关于wxss常用属性的说法错误的是()。 A. backgroundcolor用于修改背景色 B. color用于修改前景色 C. border: 3px solid blue表示宽度为3像素的蓝色实线 D. border: 3px solid blue表示长度为3像素的蓝色实线 8. 以下关于内联样式的说法错误的是()。 A. 在wxml代码中,一个view组件可以同时使用两个在wxss中定义的样式 B. style又称为行内样式,可直接将样式代码写到组件的首标签中 C. 小程序使用class属性指定样式规则,其属性值由一个或多个自定义样式类名组成,多个样式类名之间用空格分隔 D. 尽量将静态写入style中,这样可以加快渲染速度 9. 我是红色这段代码中文本的颜色将由()来决定。 A. 上述组件中的文本颜色将由js中的data数组中red的值来决定 B. red改为blue,上述组件中的文本颜色将会变成蓝色 C. “我是红色”改为“我是蓝色”,上述组件中的文本颜色将会变成蓝色 D. 上述组件中的文本颜色将由js中的color属性来决定 10. 下列关于样式作用范围的说法错误的是()。 A. wxss中定义example作用于所有拥有class="example"属性的组件 B. wxss中定义view{}作用于所有的js组件 C. wxss中定义view::after 在view组件后面插入内容 D. 页面wxss中的样式为局部样式,只作用在该页面,并且会覆盖app.wxss中相同的选择器