一、效果展示
二、功能实现的思路
本项目基于APICloud的AVM多端框架编写,因此需要想办法去构造特定数据、然后再改变数据,本项目核心业务就是课程数据的处理,难点就是课表的添加。
项目主要针对大学课表进行设计,课程很多地方存在不确定性,如课程周次数可能连续也可能不连续、上课时间可能1-2节也可能一整天、上课教室也可能不一样,因此课程的数据结构如下图。
后端需要提供当前周、当前周课程数据、添加课程等接口,当然课程数据查询是比较复杂的,这里就不讲。前端主要是将课表信息显示在指定位置,数据比较简单。
1、课程列表页面
(1)当前周课程原始数据结构
这个数据需要再重组,因为需要将课程摆放到指定位置,必须知道距离顶部距离以及自身高度。可以通过上课时间jie这个参数获取height,top。
let data = [{ 'name': '大数据可视化技术', 'room': '机房C414', 'weekday': '3', 'bg': '2', 'jie': '3-4', 'top': '140px', 'height': '140px' }]
横向分为8份,每份宽度12.5%,高度70px,默认可以显示13节课。课程背景默认有7种样式,星期对应的是left参数也有7种样式,上课节次对应top参数有12种样式具体css如下:
.bg-1 { background: linear-gradient(#facea8, #fe8979)} .bg-2 { background: linear-gradient(#dfc3fe, #90c5fb)} .bg-3 { background: linear-gradient(#9deef5, #68e1b5)} .bg-4 { background: linear-gradient(#eeba93, #dd65c7)} .bg-5 { background: linear-gradient(#e6f6c9, #68e1b5)} .bg-6 { background: linear-gradient(#dfc3fe, #dd65c7)} .bg-7 { background: linear-gradient(#c8e65f, #7abafb)} .week-1 {left: 12.5%;} .week-2 {left: 25%;} .week-3 {left: 37.5%;} .week-4 {left: 50%;} .week-5 {left: 62.5%;} .week-6 {left: 75%;} .week-0 {left: 87.5%;}
每一门课程都是用绝对定位,宽度相同,根据上课时间决定高度、位置代码表示如下
<view class="course_list"> <view v-for="(rs,key) in course_data" :key="key" :class="'course week-'+rs.weekday+' bg-'+rs.bg" :style="'height:'+rs.height+';top:'+rs.top"> <text class=" course-name">{{rs.name}}</text> <text class="course-room">{{rs.room}}</text> </view> </view>
(2)当前周星期数据结构
获取当前周的时间代码如下
this.setDate(new Date()); setDate(date) { let arr = []; let week = date.getDay() - 1; date = this.addDate(date, week * -1); this.currentFirstDate = new Date(date); for (var i = 0; i < 7; i++) { arr[i] = this.formatDate(i == 0 ? date : this.addDate(date, 1)) } this.week_data = arr this.currentWeek() }, addDate(date, n) { date.setDate(date.getDate() + n); return date; }, formatDate(date) { let year = date.getFullYear(); let month = (date.getMonth() + 1); let day = date.getDate(); let week = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'][date.getDay()]; this.currentMonth = month; if (month < 10) { month = '0' + month; } if (day < 10) { day = '0' + day; } return { "week": week, "date": month + '/' + day, "active": false }; }, //当前星期 currentWeek() { let date = new Date(); let weekday = date.getDay(); if (weekday == 1) { this.week_data[0].active = true } else if (weekday == 2) { this.week_data[1].active = true } else if (weekday == 3) { this.week_data[2].active = true } else if (weekday == 4) { this.week_data[3].active = true } else if (weekday == 5) { this.week_data[4].active = true } else if (weekday == 6) { this.week_data[5].active = true } else if (weekday == 0) { this.week_data[6].active = true } }
上一周以及下一周相应代码
last() { if (this.week > 1) { this.week = this.week - 1 this.setDate(this.addDate(this.currentFirstDate, -7)); } }, next() { if (this.week < 20) { this.week = this.week + 1 this.setDate(this.addDate(this.currentFirstDate, 7)); } },
2、课程添加页面
本项目主要针对大学课程进行设计,由于上课时间地点可能不规律,因此需要考虑周次、节次、星期、上课地点几个因素。为了方便用户快速添加课程,同一门课程可支持添加多个上课时间。页面业务逻辑只有周次选择、时间选择两个地方有点复杂因此将其拆分为两个组件<class-week></class-week> <class-time></class-time>
课程的主要字段如下
name: '大学计算机', //课程名称 room: '机房C411', //上课教室 teacher: '李国海', //任课教师 weekday: '0', //星期 weeks: '1-9,11-20', //上课周数 jie: '5-9', //上课节次 bg:'1', //课程颜色,系统提供7种颜色
大学的课程上课时间可能不规律上课周数也不一定是20周,周数大致分为单双周以及其他,周数的格式如下:
不规律的周次格式:1-9,11,13,15-20
1-20周单周的格式:1,3,5,7,9,11,13,15,17,19
1-20周双周的格式:2,4,6,8,10,12,14,16,18,20
1-20周的格式:1-20
但是这种数据格式对后端数据查询很不友好。
页面初始数据,默认1-24周全部选中。
(1)单周choose(1),双周choose(2),全选choose(0)具体业务处理源代码如下:
choose(n) { for (let i in this.weeks) { this.weeks[i].on = false } if (n == 1) { if (this.choose == 1) { this.choose = 3 } else { this.choose = 1 for (let i = 0; i < this.weeks.length; i++) { if (this.weeks[i].week % 2 != 0) { this.weeks[i].on = true } } } } else if (n == 2) { if (this.choose == 2) { this.choose = 3 } else { this.choose = 2 for (let i = 0; i < this.weeks.length; i++) { if (this.weeks[i].week % 2 == 0) { this.weeks[i].on = true } } } } else if (n == 0) { if (this.choose == 0) { this.choose = 3 } else { this.choose = 0 for (let i in this.weeks) { this.weeks[i].on = true } } } }
(2)选择某一周的函数set_week(i) 源代码如下
set_week(i) { if (this.weeks[i].on == true) { this.weeks[i].on = false } else { this.weeks[i].on = true } }
(3)确定按钮get_weeks()源代码如下
get_weeks() { this.mask = false //影藏组件 let arr = []; for (let i = 0; i < this.weeks.length; i++) { let on = this.weeks[i].on; if (on == true) { arr.push(this.weeks[i].week); } } let result = []; let tmp; while (tmp = arr.shift()) { if (result.length == 0) { result.push([tmp]); continue; } let e = result[result.length - 1]; if (tmp == e[e.length - 1] + 1) { e.push(tmp); } else { result.push([tmp]); } } for (let i = 0; i < result.length; i++) { let res = result[i]; if (res.length > 1) { result[i] = res[0] + '-' + res[res.length - 1]; } else { result[i] = res[0] + '' } } for (let i = 0; i < result.length; i++) { if (result[i].indexOf("-") != -1) { result[i] = result[i] } } this.fire('GetWeek', { weeks: result.join(',') });//格式为1-9,11,13,15-20 }
上课时间组件<class-time></class-time> 里面包含星期、上课节次数(注意上课节次数必须是连续的否则需要单独添加另一个上课时间),主要难点在于判断上课节次数是否连续。
页面初始数据,默认最大上课节次13节。
weekdays: [ { name: '星期一', on: false }, { name: '星期二', on: false }, { name: '星期三', on: false }, { name: '星期四', on: false }, { name: '星期五', on: false }, { name: '星期六', on: false }, { name: '星期日', on: false } ], times: base.class_time(), num_arr: [], mask: false, jie: '', weekday: 0 class_time() { let data = [ { 'jie': '1节', 'time': '08:30-09:15' }, { 'jie': '2节', 'time': '09:25-10:10' }, { 'jie': '3节', 'time': '10:25-11:10' }, { 'jie': '4节', 'time': '11:20-12:05' }, { 'jie': '5节', 'time': '14:00-14:45' }, { 'jie': '6节', 'time': '14:55-15:40' }, { 'jie': '7节', 'time': '15:55-16:40' }, { 'jie': '8节', 'time': '16:50-17:35' }, { 'jie': '9节', 'time': '17:45-18:30' }, { 'jie': '10节', 'time': '18:30-19:00' }, { 'jie': '11节', 'time': '19:00-19:45' }, { 'jie': '12节', 'time': '19:50-20:35' }, { 'jie': '13节', 'time': '20:45-21:30' } ] return data; }
选择上课节次数(如5-9 这里需要判断单击后选中的数据是否为连续的数字)函数set_time(index)源代码如下
set_time(index) { let jie = (index + 1); //判断是否已添加 if (this.isInArray(this.num_arr, jie)) { this.delArrItem(this.num_arr, jie); this.num_arr.sort(function (x, y) { return x - y; }); //console.log('删除' + jie) if (this.isContinuityNum(this.num_arr)) { this.times[index].td1 = false } else { //console.log('删除后不连续') this.times[index].td1 = true this.num_arr.push(jie); this.num_arr.sort(function (x, y) { return x - y; }); api.toast({ msg: '上课时间必须连续' }); } } else { this.num_arr.push(jie); this.num_arr.sort(function (x, y) { return x - y; }); if (this.isContinuityNum(this.num_arr)) { this.times[index].td1 = true } else { //console.log('增加后不连续') this.delArrItem(this.num_arr, jie); this.num_arr.sort(function (x, y) { return x - y; }); this.times[index].td1 = false api.toast({ msg: '上课时间必须连续' }); } } this.jie =this.num_arr[0]+ '-'+this.num_arr[(this.num_arr.length -1)];//格式1-2 } //删除数组元素 delArrItem(arr, item) { for (var i = 0; i < arr.length; i++) { if (arr[i] === item) { if (arr[i + 1] === item) { arr.splice(i, 1); i--; continue; } arr.splice(i, 1); } } return arr; }, //判断是否是连续的数字 isContinuityNum(array) { if (!array) { //数组为null return false; } if (array.length == 0) { //数组为[] return true; } var len = array.length; var n0 = array[0]; var sortDirection = 1;//默认升序 if (array[0] > array[len - 1]) { //降序 sortDirection = -1; } if ((n0 * 1 + (len - 1) * sortDirection) !== array[len - 1]) { //筛除['3',4,5,6,7,8] return false; } var isContinuation = true; for (var i = 0; i < len; i++) { if (array[i] !== (i + n0 * sortDirection)) { isContinuation = false; break; } } return isContinuation; }, //判断元素是否在数组里面 isInArray(arr, value) { for (var i = 0; i < arr.length; i++) { if (value == arr[i]) { return true; } } return false; }
最终前端需要提交给后端的数据格式如下:
yearId: '200', //学年id,这里是指那一学期的课程 name: '大学计算机', //课程名称 teacher: '李国海', //任课教师 bg: '1', //课程颜色,系统提供7种颜色 class_time: [ { weeks: '1-20', room: '一教A307', weekday: '1', jie: '1-2' }, { weeks: '1-20', room: '机房C405', weekday: '3', jie: '3-4' } ]