uni-app 个人课程表页面
插件参考地址
大部分代码都是参考了上述代码,只对代码做出了优化
1. 页面模板
在 schedule.vue
文件中,编写页面结构:
html
<template>
<view>
<u-navbar title="个人中心">
<view class="u-nav-slot" slot="left">
<u-icon name="arrow-left" size="19" @click="back"></u-icon>
</view>
<view class="u-nav-slot" slot="center">
<picker class="week" style="height: 100%;" @change="onPickerChange" :value="currentWeek-1" :range="weekOptions">
<view>
<text style="color: #d3dbff">第{{ currentWeek }}周</text>
<uni-icons type="down" size="20" style="margin-left: 10rpx;" :color="themeColor"></uni-icons>
</view>
</picker>
</view>
<view class="u-nav-slot" slot="right">
<u-icon name="settings" size="19"></u-icon>
</view>
</u-navbar>
<!-- 日期列表 -->
<view style="display: flex; flex-direction: row; margin-top: 60px">
<view style="font-size: 18px; width: 35px; text-align: center; display: flex; justify-content: center; align-items: center; font-weight: 600; color: #d3dbff">
{{ currentMonth }}
</view>
<view class="day">
<view v-for="(day, index) in dayList" :key="index" class="day-text">
<view style="margin-top: 5px; color: #909399">周{{ day.day }}</view>
<view style="margin-top: 2.5px; color: #909399; font-size: 10px;">{{ day.date }}</view>
</view>
</view>
</view>
<!-- 课程滚动区域 -->
<view class="scroll">
<view style="width: 750rpx; display: flex;">
<view style="color: #d3dbff">
<view v-for="(pitch, index) in pitchNumbers" :key="index" class="left">
<view style="display: flex; flex-direction: column; justify-content: center; align-items: center;">
<view>{{ pitch }}</view>
<view style="color: #909399; font-size: 10px;">
<view>{{ timeMap[pitch - 1].start }}</view>
<view>{{ timeMap[pitch - 1].end }}</view>
</view>
</view>
</view>
</view>
<view v-for="(pitch, index) in pitchNumbers" :key="index">
<view :style="{
width: '750rpx',
marginTop: (index + 1) * 120 + 'rpx',
position: 'absolute',
borderBottom: '1px solid rgba(200, 200, 200, 0.1)',
left: '0'
}"></view>
</view>
<view style="width: calc(100% - 70rpx); position: relative;">
<view v-for="(item, index) in currentSchedule" :key="index" @click="openDetail(item)">
<view class="flex-item class-item" :style="{
marginRight: '5rpx',
marginLeft: `calc((${item.day - 1}) * 100% / 7 + 5rpx)`,
marginTop: ((item.startPeriod - 1) * 60 + 2.5) * 2 + 'rpx',
marginBottom: '5rpx',
height: (((item.endPeriod - item.startPeriod + 1)) * 60 - 5) * 2 + 'rpx',
backgroundColor: colorArray[((item.endPeriod + item.day) * item.name.length) % 7]
}">
<view class="text">
<view style="padding-left: 4rpx; margin-right: 4rpx;">
{{ formatClassText(item) }} @{{ item.room }} @{{ item.attendance }}
</view>
</view>
</view>
</view>
</view>
</view>
</view>
<!-- 弹出层 -->
<uni-popup ref="popup" background-color="none">
<view class="context">
<view class="info">
<view style="padding: 10rpx 20rpx;">
<view class="name" style="color: #303133; font-size: 18px; font-weight: bold;">
{{ selected.name }}
</view>
<view class="less">
<view class="item">
<uni-icons type="calendar" size="20"></uni-icons>
<text class="label">节次:</text> <text class="value">{{ selected.startPeriod }}-{{ selected.endPeriod }}节</text>
</view>
<view class="item">
<uni-icons type="auth-filled" size="20"></uni-icons>
<text class="label">老师:</text> <text class="value">{{ selected.teacher }}</text>
</view>
<view class="item">
<uni-icons type="flag" size="20"></uni-icons>
<text class="label">教室:</text> <text class="value">{{ selected.room }}</text>
</view>
</view>
</view>
</view>
</view>
</uni-popup>
</view>
</template>
3. 页面逻辑
在 script
部分实现页面的逻辑:
javascript
<script>
import dayjs from 'dayjs';
export default {
data() {
return {
themeColor: "#d3dbff",
currentWeek: 1,
statusBarHeight: 0,
topHeight: 0,
weekOptions: ['第一周', '第二周', '第三周', '第四周', '第五周', '第六周', '第七周', '第八周', '第九周', '第十周', '第十一周', '第十二周', '第十三周', '第十四周', '第十五周', '第十六周', '第十七周', '第十八周'],
dayList: [],
currentMonth: "",
startingDate: { y: 2024, m: 9, d: 10 },
timeMap: [
{ start: '8:00', end: '8:45' },
{ start: '8:55', end: '9:40' },
{ start: '10:00', end: '10:45' },
{ start: '10:55', end: '11:40' },
{ start: '14:20', end: '15:15' },
{ start: '15:25', end: '16:10' },
{ start: '16:30', end: '17:15' },
{ start: '17:25', end: '18:10' },
{ start: '19:00', end: '19:45' },
{ start: '19:55', end: '20:40' },
{ start: '20:50', end: '21:35' },
{ start: '20:45', end: '21:20' }
],
currentSchedule: [ /* 课程安排数据 */ ],
pitchNumbers: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"],
colorArray: ['#ff55ff', '#00aaff', '#aa5500', '#00aa00', '#aaaaff', '#da786b', '#5297f6', '#aa007f', '#7ac077', '#6ec8bf'],
selected: { name: '', teacher: '', room: '' }
}
},
onLoad() {
this.calculateDayInfo();
this.calculateCurrentWeek();
},
methods: {
calculateDayInfo() {
// 计算当前周的日期信息
const dayArray = [7, 1, 2, 3, 4, 5, 6];
const today = new Date();
const currentMonth = today.getMonth() + 1; // 当前月份(1-12)
this.currentMonth = this.formatNumber(currentMonth, 2); // 格式化月份为两位数
const currentDay = today.getDate(); // 当前日期(1-31)
const currentTime = today.getTime();
const weekDay = dayArray[today.getDay()]; // 获取当前星期几(1-7)
const oneDayTime = 24 * 60 * 60 * 1000; // 一天的毫秒数
const mondayTime = currentTime - (weekDay - 1) * oneDayTime; // 获取上一个星期一的时间
const dates = [];
for (let i = 0; i < 7; i++) {
const date = dayjs(mondayTime).add(i, 'day');
dates.push({
day: this.getDayName(i),
date: `${this.formatNumber(date.month() + 1, 2)}/${this.formatNumber(date.date(), 2)}`
});
}
this.dayList = dates; // 更新日期列表
},
calculateCurrentWeek() {
// 计算当前周数
const today = new Date();
const currentYear = today.getFullYear();
const currentMonth = today.getMonth() + 1;
const currentDate = today.getDate();
this.currentWeek = this.calculateWeekNumber(this.startingDate.y, this.startingDate.m, this.startingDate.d,
currentYear, currentMonth, currentDate);
},
// 计算周数的辅助函数
calculateWeekNumber(startYear, startMonth, startDay, currentYear, currentMonth, currentDay) {
const dayArray = [1, 2, 3, 4, 5, 6, 0]; // 将星期几转为数组
const startDate = new Date(startYear, startMonth - 1, startDay); // 初始化开始日期
const endDate = new Date(currentYear, currentMonth - 1, currentDay); // 初始化结束日期
const startDayOfWeek = startDate.getDay(); // 获取开始日期是星期几
const adjustedStartDay = dayArray.indexOf(startDayOfWeek); // 将星期几转换为 1-7
const endDayOfWeek = endDate.getDay();
const adjustedEndDay = dayArray.indexOf(endDayOfWeek);
const daysUntilEnd = 7 - adjustedEndDay; // 计算结束日期到下一个星期一的天数
const adjustedStartDate = new Date(startYear, startMonth - 1, startDay - adjustedStartDay); // 调整开始日期到本周一
const adjustedEndDate = new Date(currentYear, currentMonth - 1, currentDay + daysUntilEnd); // 调整结束日期到下周一
const startTime = adjustedStartDate.getTime();
const endTime = adjustedEndDate.getTime();
// 当前时间 - 开始时间 = 当前周数
const weekNumber = (Math.abs(endTime - startTime) / 1000 / 60 / 60 / 24 / 7);
return weekNumber;
},
onPickerChange(event) {
// 处理选择周次的变化
const selectedWeekIndex = parseInt(event.detail.value); // 选择的周次索引
this.currentWeek = selectedWeekIndex + 1; // 更新当前周数
// 将 startingDate 对象转换为 dayjs 日期
const startingDate = dayjs(`${this.startingDate.y}-${this.startingDate.m}-${this.startingDate.d}`);
if (!startingDate.isValid()) {
console.error("Invalid starting date");
return; // 如果日期无效,则退出
}
// 计算当前周的开始日期(周一)
const weekDifference = this.currentWeek - 1; // 当前周与起始周的差
const mondayTime = startingDate.add(weekDifference, 'week').startOf('week'); // 获取当前周的周一
// 更新 currentMonth
this.currentMonth = mondayTime.month() + 1; // month() 返回的是 0-11,所以加 1
const dates = [];
for (let i = 0; i < 7; i++) {
const dayTime = mondayTime.add(i, 'day'); // 获取当前周的每一天
dates.push({
day: this.getDayName(i), // 获取星期名称
date: `${this.formatNumber(dayTime.month() + 1, 2)}/${this.formatNumber(dayTime.date(), 2)}` // 格式化日期
});
}
this.dayList = dates; // 更新日期列表
console.log(this.dayList); // 检查更新的日期列表
},
formatClassText(item) {
// 格式化课程文本
return ('' + num).length < length ? ((new Array(length + 1)).join('0') + num).slice(-length) : '' + num;
},
// 获取星期名称
getDayName(index) {
const days = ['一', '二', '三', '四', '五', '六', '日'];
return days[index];
},
openDetail(item) {
this.selected = item;
this.$refs.popup.open('center');
},
back() {
uni.navigateBack();
}
}
}
</script>
4. 样式设计
在 schedule.scss
文件中添加样式:
scss
.top {
position: relative;
width: 750rpx;
background-color: #fff;
.container {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
}
.left {
margin-right: auto;
text-align: left;
}
.center {
flex: 1;
text-align: center;
}
.right {
text-align: right;
}
}
.day {
display: flex;
height: 70rpx;
flex-direction: row;
}
.day-text {
width: calc((750rpx - 70rpx) / 7);
height: 62rpx;
font-weight: 600;
font-size: 26rpx;
justify-content: center;
flex-direction: column;
display: flex;
align-items: center;
}
.scroll {
height: 1170rpx;
z-index: 101;
}
.left {
width: 70rpx;
height: 120rpx;
font-size: 26rpx;
justify-content: center;
display: flex;
align-items: center;
}
.class-item {
width: calc(100% / 7 - 10rpx);
position: absolute;
box-sizing: border-box;
display: flex;
border-radius: 16rpx;
font-size: 20rpx;
color: #ffffff;
overflow: hidden;
}
.text {
width: calc(680rpx / 7 - 10rpx);
padding-top: 10rpx;
}
.context {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 15rpx;
height: auto;
width: 750rpx;
.info {
position: relative;
width: 562rpx;
height: auto;
background-color: #fff;
border-radius: 4px;
padding: 15rpx;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
.name {
color: #303133;
font-size: 18px;
font-weight: bold;
margin-bottom: 10rpx;
}
.item {
width: 100%;
height: 40rpx;
display: flex;
margin-left: 20rpx;
justify-content: flex-start;
align-items: center;
}
.label {
font-size: 16px;
color: #606266;
}
.value {
color: #aeb0b4;
}
}
5. 运行
完成上述步骤后,您可以在开发工具中运行项目,查看个人课程表页面的效果。确保所有功能正常,如周次选择、课程详情弹出层等。