课程表功能的实现
思路
课程表的实现,我想了很久,在没有现成组件库的情况下实现,我主要是用了vant weapp的宫格和单选框来实现,主要的难点有以下两个方面:
1.节数与天数和grid内单选框索引的转换
返回值给我一个包含课程节数和天数的对象数组,我把它转换成一个包含unit(5节课*7天的对应的数字来定位)的对象数组
2.单选框样式的魔改
单选框直接使用radio的插槽来贴图来适应样式需求
<van-radio-group :value="unit" @change="addClass">
<van-grid :border="false" :column-num="7" :clickable="true">
<van-grid-item v-for="value in 35" :key="value" use-slot custom-class="editCourse">
<van-button type="primary" color="#006600" v-show="map[value]!==-1?true:false"
@click="editCourse(value)"
custom-style="padding-left:5rpx;line-height:35rpx;padding-right:5rpx;padding-top:5rpx;height:203rpx;margin: 0 auto;width:90rpx;border-radius: 10rpx;font-size:25rpx!important;font-weight:bold">
<div>{{InfoMap[value].courseName}}</div>
</van-button>
<van-radio use-icon-slot :value="unit" :name="value" v-show="map[value]!==-1?false:true">
<image style="margin-top:10rpx;height:190rpx;width:90rpx;" slot="icon"
:src="unit===value?icon.choosed:icon.unChoosed" />
</van-radio>
</van-grid-item>
</van-grid>
</van-radio-group>
这样就看不出来是单选框了
温馨提示:尽量用原生
其他主要是布局和样式,没啥好说的.吐槽一句,vant组件库的插槽不是真的放在组件里面,而是悬浮在它上面,有时候会出现错位的情况.
所以能用原生尽量用原生,组件样式带原生样式,样式穿透和!important都不起作用,不太好把握(主要是我太菜了,css好难)
源码
Table.vue:
<template>
<view>
<bar :nav="setNav" />
<van-popup :show="chooseWeek" @close="onCloseChooseWeek" position="bottom" custom-class="height:50%">
<van-picker show-toolbar title="请选择现在的周数" :columns="weeks" @cancel="cancelChooseWeek" @confirm="confirmWeek"
:loading="loadingTable" />
</van-popup>
<van-row>
<van-col span="22" offset="2">
<van-grid :border="false" :column-num="7" :clickable="true">
<van-grid-item use-slot custom-class="week">
<van-tag type="primary" color="#006600" custom-class="weekTag">
周一
</van-tag>
</van-grid-item>
<van-grid-item use-slot custom-class="week">
<van-tag type="primary" color="#006600" custom-class="weekTag">
周二
</van-tag>
</van-grid-item>
<van-grid-item use-slot custom-class="week">
<van-tag type="primary" color="#006600" custom-class="weekTag">
周三
</van-tag>
</van-grid-item>
<van-grid-item use-slot custom-class="week">
<van-tag type="primary" color="#006600" custom-class="weekTag">
周四
</van-tag>
</van-grid-item>
<van-grid-item use-slot custom-class="week">
<van-tag type="primary" color="#006600" custom-class="weekTag">
周五
</van-tag>
</van-grid-item>
<van-grid-item use-slot custom-class="week">
<van-tag type="primary" color="#006600" custom-class="weekTag">
周六
</van-tag>
</van-grid-item>
<van-grid-item use-slot custom-class="week">
<van-tag type="primary" color="#006600" custom-class="weekTag">
周日
</van-tag>
</van-grid-item>
</van-grid>
</van-col>
</van-row>
<van-row>
<van-col span="2">
<van-grid :border="false" :column-num="1" :clickable="true">
<van-grid-item use-slot v-for="value in 5" :key="value" custom-class="courseNum">
<div class="course">
<van-button type="primary" color="#006600" block custom-class="courseNumBtn">
{{value+1}}
</van-button>
</div>
</van-grid-item>
</van-grid>
</van-col>
<van-radio-group :value="unit" @change="addClass">
<van-grid :border="false" :column-num="7" :clickable="true">
<van-grid-item v-for="value in 35" :key="value" use-slot custom-class="editCourse">
<van-button type="primary" color="#006600" v-show="map[value]!==-1?true:false"
@click="editCourse(value)"
custom-style="padding-left:5rpx;line-height:35rpx;padding-right:5rpx;padding-top:5rpx;height:203rpx;margin: 0 auto;width:90rpx;border-radius: 10rpx;font-size:25rpx!important;font-weight:bold">
<div>{{InfoMap[value].courseName}}</div>
</van-button>
<van-radio use-icon-slot :value="unit" :name="value" v-show="map[value]!==-1?false:true">
<image style="margin-top:10rpx;height:190rpx;width:90rpx;" slot="icon"
:src="unit===value?icon.choosed:icon.unChoosed" />
</van-radio>
</van-grid-item>
</van-grid>
</van-radio-group>
<van-notify id="van-notify" />
</van-row>
</view>
</template>
<script>
import bar from '../../components/bar.vue';
import Notify from '../../wxcomponents/vant/dist/notify/notify';
export default {
components: {
bar
},
data() {
return {
loadingTable: false,
courseMap: [],
map: [],
InfoMap: [],
weeks: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20],
unit: null,
showBtn: false,
chooseWeek: false,
week: 1,
icon: {
unChoosed: "/static/Btn/addBtn.png",
choosed: "/static/Btn/addBtn_HL.png"
},
setNav: {
'bg': '#006600',
'color': "#fff",
'isdisPlayNavTitle': true,
'navTitle': '课表'
},
windowHeight: null,
weekHeight: null,
courseHeight: null,
btnHeight: null
}
},
mounted() {
let token = ""
token = wx.getStorageSync('token')
if (token !== "") {
this.loadDefaultData()
} else {
this.login()
}
},
onLoad() {
uni.$on('choose', (chooseWeek) => {
this.chooseWeek = chooseWeek
})
},
onShow() {
this.loadDefaultData()
},
onUnload() {
uni.$off('choose')
},
methods: {
login() {
let self = this
wx.login({
success(res) {
wx.request({
url: getApp().globalData.baseURL + "/login",
data: {
"code": res.code
},
success(res) {
if (res.data.code === 1) {
wx.setStorageSync('openid', res.data.data.openid)
wx.setStorageSync('token', res.data.data.token)
self.loadDefaultData()
} else {
Notify({
type: 'danger',
message: '获取用户信息失败,请重试'
});
}
},
fail(err) {
Notify({
type: 'danger',
message: '连接服务器失败'
});
}
})
},
fail(err) {
Notify({
type: 'danger',
message: '获取用户登录态失败'
});
}
})
},
editCourse(value) {
var Info = this.InfoMap[value]
uni.$on('Send', () => {
uni.$emit('courseInfo', Info)
})
uni.navigateTo({
url: "../courseInfo/courseInfo"
})
},
// 关闭选择周数弹出层
onCloseChooseWeek() {
this.chooseWeek = false
},
cancelChooseWeek(e) {
this.chooseWeek = false
},
// 计算并得到一个包含课程信息的数组
getMap(data) {
var map = new Array()
for (let i = 0; i < data.length; i++) {
map[i] = {
unit: (data[i].jieshu - 1) * 7 + data[i].day - 1,
courseName: data[i].courseName,
teacher: data[i].teacher,
classroom: data[i].classroom,
weeks: data[i].weeks
}
}
this.courseMap = map
},
resetMap(courseMap) {
// 拿到一个排序的数组
var map = new Array()
for (let i = 0; i < courseMap.length; i++) {
map[i] = courseMap[i].unit
}
var compare = function(x, y) {
if (x < y) {
return -1;
} else if (x > y) {
return 1;
} else {
return 0;
}
}
map.sort(compare)
var Map = new Array(35).fill(-1)
for (let i = 0; i < map.length; i++) {
Map[map[i]] = map[i]
}
this.map = Map
},
getInfoMap(courseMap) {
var map = new Array()
function compare(prop) {
return function(x, y) {
var v1 = x[prop];
var v2 = y[prop];
return v1 - v2
}
}
map = courseMap.sort(compare('unit'))
// console.log(map)
var Map = new Array(35).fill({})
for (let i = 0; i < map.length; i++) {
Map[map[i].unit] = {
unit: map[i].unit,
teacher: map[i].teacher,
classroom: map[i].classroom,
courseName: map[i].courseName,
weeks: map[i].weeks
}
}
this.InfoMap = Map
},
loadDefaultData() {
let self = this
uni.request({
url: getApp().globalData.baseURL + "/course/selectByOpenidAndWeeks",
data: {
"openid": wx.getStorageSync('openid'),
"week": `${this.week}`
},
header: {
"token": wx.getStorageSync('token')
},
success: (res) => {
self.loadingTable = false
if (res.data.code == 1) {
self.getMap(res.data.data)
self.resetMap(self.courseMap)
self.getInfoMap(self.courseMap)
} else if (res.data.code == 0) {
Notify({
type: 'warning',
message: '您尚未登录,无法获取课表哦',
top: 64
});
} else {
Notify({
type: 'danger',
message: '获取课表失败',
top: 64
});
}
this.chooseWeek = false
},
fail: (err) => {
self.loadingTable = false
Notify({
type: 'danger',
message: '获取课表失败,请检查您的网络连接',
top: 64
});
this.chooseWeek = false
},
})
},
confirmWeek(e) {
this.week = e.detail.value
this.loadingTable = true
let self = this
uni.request({
url: getApp().globalData.baseURL + "/course/selectByOpenidAndWeeks",
data: {
"openid": wx.getStorageSync('openid'),
"week": `${this.week}`
},
header: {
"token": wx.getStorageSync('token')
},
success: (res) => {
self.loadingTable = false
if (res.data.code == 1) {
self.getMap(res.data.data)
self.resetMap(self.courseMap)
self.getInfoMap(self.courseMap)
Notify({
type: 'success',
message: '获取课表成功!',
top: 64
});
} else if (res.data.code == 0) {
Notify({
type: 'warning',
message: '您尚未登录,无法获取课表哦',
top: 64
});
} else {
Notify({
type: 'danger',
message: '获取课表失败',
top: 64
});
}
this.chooseWeek = false
console.log(res.data)
},
fail: (err) => {
self.loadingTable = false
Notify({
type: 'danger',
message: '获取课表失败,请检查您的网络连接',
top: 64
});
this.chooseWeek = false
},
})
},
// 添加课程
addClass(e) {
this.unit = e.detail
uni.navigateTo({
url: "../addCourse/addCourse"
})
uni.$on('send', () => {
uni.$emit('update', {
msg: this.unit
})
})
}
}
}
</script>
<style>
.month {
/* height: 60rpx; */
font-size: 32rpx;
}
.week {
height: 60rpx;
text-align: center;
}
.courseNum {
height: 210rpx;
text-align: center;
}
.courseNumBtn {
height: 203rpx !important;
width: 20rpx !important;
border-radius: 10rpx !important;
}
.editCourse {
height: 210rpx;
}
.weekTag {
width: 60rpx;
height: 50rpx;
text-align: center;
font-weight: bold;
}
.course {
width: 100rpx;
margin-right: 20rpx;
}
</style>
自定义顶部导航栏组件:
<template>
<view>
<view class="header"
:style="{'height':titleBarHeight,'padding-top':statusBarHeight,'background-color': nav.bg}">
<text class="iconfont header-back " :style="{'border':nav.color}" v-if="nav.isdisPlayNavTitle"
@click="popChooseWeek">

</text>
<view class="header-title ">{{nav.navTitle}}</view>
</view>
<view :style="{'height':titleBarHeight,'padding-top':statusBarHeight}"></view>
</view>
</template>
<script>
export default {
props: ["nav"],
data() {
return {
chooseWeek: false,
statusBarHeight: 0,
titleBarHeight: 0,
}
},
created() {
var that = this;
uni.getSystemInfo({
success: function(res) {
if (res.model.indexOf('iPhone') !== -1) {
that.titleBarHeight = 44 + 'px';
} else {
that.titleBarHeight = 48 + 'px';
}
that.statusBarHeight = res.statusBarHeight + 'px'
},
})
},
methods: {
// 弹出选择周数框
popChooseWeek() {
this.chooseWeek = true
uni.$emit('choose', this.chooseWeek)
}
}
}
</script>
<style>
.header {
display: flex;
align-items: center;
top: 0;
position: fixed;
width: 100%;
z-index: 100;
left: 0;
}
.header .header-title {
position: absolute;
left: 50%;
font-size: 31rpx;
transform: translateX(-50%);
color: #fff
}
.header-back {
position: absolute;
left: 15upx;
font-size: 30upx;
padding: 10upx;
border-radius: 50%;
}
@font-face {
font-family: 'iconfont';
/* Project id 2568587 */
src: url('//at.alicdn.com/t/font_2568587_gslu7gcw01.woff2?t=1621863393001') format('woff2'),
url('//at.alicdn.com/t/font_2568587_gslu7gcw01.woff?t=1621863393001') format('woff'),
url('//at.alicdn.com/t/font_2568587_gslu7gcw01.ttf?t=1621863393001') format('truetype');
}
.iconfont {
font-family: "iconfont" !important;
font-size: 25px !important;
color: #fff !important
}
.weight {
font-weight: bold;
}
</style>
效果: