peixue-dev/peidu/uniapp/provider-package/pages/provider/schedule.vue

470 lines
11 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="schedule-page">
<!-- 日历视图 -->
<view class="calendar-section">
<view class="calendar-header">
<button class="nav-btn" @click="prevMonth"></button>
<text class="current-month">{{ currentMonth }}</text>
<button class="nav-btn" @click="nextMonth"></button>
</view>
<view class="calendar-weekdays">
<text v-for="day in weekdays" :key="day" class="weekday">{{ day }}</text>
</view>
<view class="calendar-days">
<view
v-for="(day, index) in calendarDays"
:key="index"
class="day-cell"
:class="{
'other-month': day.isOtherMonth,
'today': day.isToday,
'selected': day.isSelected,
'has-course': day.hasCourse
}"
@click="selectDate(day)"
>
<text class="day-number">{{ day.day }}</text>
<view v-if="day.courseCount > 0" class="course-dot">
<text>{{ day.courseCount }}</text>
</view>
</view>
</view>
</view>
<!-- 当日课程列表 -->
<view class="course-list-section">
<view class="section-header">
<text class="section-title">{{ selectedDateText }}的课程</text>
<text class="course-count">{{ dayCourses.length }}节</text>
</view>
<view v-if="dayCourses.length > 0" class="course-list">
<view
v-for="course in dayCourses"
:key="course.id"
class="course-item"
@click="goCourseDetail(course.id)"
>
<view class="course-time">
<text class="time">{{ course.time }}</text>
</view>
<view class="course-info">
<text class="course-name">{{ course.courseName }}</text>
<text class="student-name">👨‍🎓 {{ course.studentName }}</text>
<text class="location">📍 {{ course.location }}</text>
</view>
<view class="course-status" :class="'status-' + course.status">
{{ course.statusText }}
</view>
</view>
</view>
<view v-else class="empty-state">
<text class="empty-icon">📅</text>
<text class="empty-text">这天没有课程安排</text>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
currentYear: 0,
currentMonth: '',
selectedDate: null,
selectedDateText: '',
weekdays: ['日', '一', '二', '三', '四', '五', '六'],
calendarDays: [],
dayCourses: [],
courseData: {} // 存储每天的课程数据
}
},
onLoad() {
this.initCalendar()
this.loadCourseData()
},
methods: {
initCalendar() {
const now = new Date()
this.currentYear = now.getFullYear()
const month = now.getMonth() + 1
this.currentMonth = `${this.currentYear}${month}`
this.selectedDate = now
this.selectedDateText = `${month}${now.getDate()}`
this.generateCalendar(this.currentYear, month)
},
generateCalendar(year, month) {
const firstDay = new Date(year, month - 1, 1)
const lastDay = new Date(year, month, 0)
const daysInMonth = lastDay.getDate()
const startWeekday = firstDay.getDay()
const days = []
const today = new Date()
// 上个月的日期
const prevMonthLastDay = new Date(year, month - 1, 0).getDate()
for (let i = startWeekday - 1; i >= 0; i--) {
days.push({
day: prevMonthLastDay - i,
isOtherMonth: true,
hasCourse: false,
courseCount: 0
})
}
// 当月日期
for (let i = 1; i <= daysInMonth; i++) {
const date = new Date(year, month - 1, i)
const dateStr = `${year}-${String(month).padStart(2, '0')}-${String(i).padStart(2, '0')}`
const courseCount = this.courseData[dateStr] || 0
days.push({
day: i,
date: date,
dateStr: dateStr,
isOtherMonth: false,
isToday: date.toDateString() === today.toDateString(),
isSelected: this.selectedDate && date.toDateString() === this.selectedDate.toDateString(),
hasCourse: courseCount > 0,
courseCount: courseCount
})
}
// 下个月的日期
const remainingDays = 42 - days.length
for (let i = 1; i <= remainingDays; i++) {
days.push({
day: i,
isOtherMonth: true,
hasCourse: false,
courseCount: 0
})
}
this.calendarDays = days
},
async loadCourseData() {
try {
const res = await this.$api.providerApi.getSchedule({
startDate: this.currentMonth,
type: 'month'
})
if (res.code === 200 && res.data) {
this.courseData = res.data.courseData || {}
}
} catch (error) {
console.error('加载课程数据失败:', error)
this.courseData = {}
}
this.generateCalendar(this.currentYear, parseInt(this.currentMonth.split('年')[1]))
this.loadDayCourses()
},
async loadDayCourses() {
if (!this.selectedDate) return
try {
const res = await this.$api.providerApi.getSchedule({
date: this.selectedDate,
type: 'day'
})
if (res.code === 200 && res.data) {
this.dayCourses = res.data.courses || []
}
} catch (error) {
console.error('加载当天课程失败:', error)
this.dayCourses = []
}
},
selectDate(day) {
if (day.isOtherMonth) return
this.selectedDate = day.date
const month = day.date.getMonth() + 1
const date = day.date.getDate()
this.selectedDateText = `${month}${date}`
this.generateCalendar(this.currentYear, parseInt(this.currentMonth.split('年')[1]))
this.loadDayCourses()
},
prevMonth() {
let [year, month] = this.currentMonth.split('年')
year = parseInt(year)
month = parseInt(month) - 1
if (month < 1) {
month = 12
year--
}
this.currentYear = year
this.currentMonth = `${year}${month}`
this.generateCalendar(year, month)
},
nextMonth() {
let [year, month] = this.currentMonth.split('年')
year = parseInt(year)
month = parseInt(month) + 1
if (month > 12) {
month = 1
year++
}
this.currentYear = year
this.currentMonth = `${year}${month}`
this.generateCalendar(year, month)
},
goCourseDetail(id) {
uni.navigateTo({ url: `/pages/provider/course-detail?id=${id}` })
}
}
}
</script>
<style lang="scss" scoped>
@import '@/static/css/common.scss';
.schedule-page {
min-height: 100vh;
background: $bg-color;
}
.calendar-section {
background: #fff;
padding: 30rpx;
margin-bottom: 20rpx;
.calendar-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30rpx;
.nav-btn {
width: 60rpx;
height: 60rpx;
background: $bg-color;
border-radius: 30rpx;
font-size: 32rpx;
color: $text-color;
border: none;
display: flex;
align-items: center;
justify-content: center;
}
.current-month {
font-size: 32rpx;
font-weight: bold;
color: $text-color;
}
}
.calendar-weekdays {
display: grid;
grid-template-columns: repeat(7, 1fr);
margin-bottom: 20rpx;
.weekday {
text-align: center;
font-size: 24rpx;
color: $text-secondary;
padding: 10rpx 0;
}
}
.calendar-days {
display: grid;
grid-template-columns: repeat(7, 1fr);
gap: 10rpx;
}
.day-cell {
position: relative;
aspect-ratio: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
border-radius: 8rpx;
&.other-month {
.day-number {
color: $text-placeholder;
}
}
&.today {
background: rgba($primary-color, 0.1);
.day-number {
color: $primary-color;
font-weight: bold;
}
}
&.selected {
background: $primary-color;
.day-number {
color: #fff;
}
.course-dot {
background: #fff;
color: $primary-color;
}
}
.day-number {
font-size: 26rpx;
color: $text-color;
}
.course-dot {
position: absolute;
bottom: 4rpx;
width: 32rpx;
height: 32rpx;
background: $secondary-color;
color: #fff;
border-radius: 50%;
font-size: 20rpx;
display: flex;
align-items: center;
justify-content: center;
}
}
}
.course-list-section {
padding: 30rpx;
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20rpx;
.section-title {
font-size: 32rpx;
font-weight: bold;
color: $text-color;
}
.course-count {
font-size: 26rpx;
color: $primary-color;
font-weight: bold;
}
}
}
.course-list {
display: flex;
flex-direction: column;
gap: 20rpx;
}
.course-item {
display: flex;
background: #fff;
border-radius: 16rpx;
padding: 24rpx;
gap: 20rpx;
.course-time {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-width: 100rpx;
.time {
font-size: 28rpx;
font-weight: bold;
color: $primary-color;
}
}
.course-info {
flex: 1;
display: flex;
flex-direction: column;
gap: 8rpx;
.course-name {
font-size: 28rpx;
font-weight: bold;
color: $text-color;
}
.student-name,
.location {
font-size: 24rpx;
color: $text-secondary;
}
}
.course-status {
display: flex;
align-items: center;
padding: 8rpx 16rpx;
border-radius: 20rpx;
font-size: 22rpx;
height: fit-content;
&.status-pending {
background: rgba($warning-color, 0.1);
color: $warning-color;
}
&.status-active {
background: rgba($success-color, 0.1);
color: $success-color;
}
&.status-completed {
background: rgba(0, 0, 0, 0.05);
color: $text-secondary;
}
}
}
.empty-state {
padding: 120rpx 0;
text-align: center;
.empty-icon {
display: block;
font-size: 100rpx;
margin-bottom: 20rpx;
opacity: 0.3;
}
.empty-text {
font-size: 28rpx;
color: $text-secondary;
}
}
</style>