311 lines
9.4 KiB
Vue
311 lines
9.4 KiB
Vue
<script setup lang="ts">
|
||
import { onShow } from '@dcloudio/uni-app'
|
||
import FullCalendar from '@fullcalendar/vue3'
|
||
import timeGridPlugin from '@fullcalendar/timegrid'
|
||
import interactionPlugin from '@fullcalendar/interaction'
|
||
import listPlugin from '@fullcalendar/list'
|
||
import dayjs from 'dayjs'
|
||
|
||
import { onMounted, ref } from 'vue'
|
||
import type {
|
||
CalendarOptions,
|
||
} from '@fullcalendar/core'
|
||
import cusSelects from '~/components/cus-selects-fan.vue'
|
||
import { usePageStore } from '~/stores/modules/page'
|
||
import { useAppStore } from '~/stores/modules/app'
|
||
import { type BuildInfo, type ClassroomCourseInfo, type ClassroomInfo, getBuildListAPI, getClassroomCourseListAPI, getClassroomListAPI } from '~/services/course'
|
||
|
||
const { startDate, campusList, campusId, buildId, classroomId } = storeToRefs(useAppStore())
|
||
const { setPageConfig } = usePageStore()
|
||
onShow(() => {
|
||
setPageConfig({
|
||
showNavBar: true,
|
||
pageTitle: '教室课表',
|
||
showBackAction: true,
|
||
})
|
||
})
|
||
// #ifdef H5
|
||
const campus = ref('')
|
||
const buildList = ref<BuildInfo[]>([])
|
||
const classroomList = ref<ClassroomInfo[]>([])
|
||
|
||
const getClassroomList = async () => {
|
||
if (buildId.value) {
|
||
uni.showLoading({ title: '加载中~' })
|
||
const res = await getClassroomListAPI(buildId.value)
|
||
if (res.code === 200)
|
||
classroomList.value = res.data
|
||
uni.hideLoading()
|
||
uni.showToast({ icon: res.code === 200 ? 'success' : 'error', title: res.msg })
|
||
}
|
||
}
|
||
|
||
const getBuildList = async () => {
|
||
if (campusId.value) {
|
||
uni.showLoading({ title: '加载中~' })
|
||
const res = await getBuildListAPI(campus.value)
|
||
if (res.code === 200)
|
||
buildList.value = res.data
|
||
uni.hideLoading()
|
||
uni.showToast({ icon: res.code === 200 ? 'success' : 'error', title: res.msg })
|
||
}
|
||
}
|
||
|
||
const onCampusSelectChange = async (e: string) => {
|
||
campus.value = e
|
||
await getBuildList()
|
||
}
|
||
const onBuildSelectChange = async (e: string) => {
|
||
buildId.value = e
|
||
await getClassroomList()
|
||
}
|
||
|
||
/** 当前事件列表 */
|
||
const currentEvents = ref([])
|
||
/** 日历对象 */
|
||
const fullCalendar = ref()
|
||
const courseList = ref<ClassroomCourseInfo[]>([])
|
||
|
||
const getClassroomCourses = async () => {
|
||
const startTime = dayjs(startDate.value).unix()
|
||
// const start = dayjs(fullCalendar.value.getApi().view.activeStart).unix()
|
||
const endTime = dayjs(fullCalendar.value.getApi().view.activeEnd).unix()
|
||
const week = Math.floor((endTime - startTime) / (60 * 60 * 24 * 7))
|
||
if (classroomId.value) {
|
||
const res = await getClassroomCourseListAPI(classroomId.value, week)
|
||
|
||
if (res.code === 200) {
|
||
courseList.value = res.data
|
||
if (res.data) {
|
||
fullCalendar.value.getApi().removeAllEvents() // 清除所有事件
|
||
currentEvents.value = courseList.value.map(data => ({
|
||
id: data.id,
|
||
title: `${data.course} - ${data.teacher}`,
|
||
start: data.startTime.replace('T', ' ').replace('Z', ''),
|
||
end: data.endTime.replace('T', ' ').replace('Z', ''),
|
||
extendedProps: {
|
||
classnames: data.classnames,
|
||
campus: data.campus,
|
||
build: data.build,
|
||
},
|
||
}))
|
||
fullCalendar.value.getApi().addEventSource(currentEvents.value) // 添加新的事件源
|
||
}
|
||
}
|
||
fullCalendar.value.getApi().render() // 手动刷新日历
|
||
}
|
||
}
|
||
const onClassroomSelectChange = async (e: string) => {
|
||
classroomId.value = e
|
||
await getClassroomCourses()
|
||
}
|
||
/** 自定义上周按钮点击事件 */
|
||
const preWeekCustomClick = async () => {
|
||
fullCalendar.value.getApi().prev() // 切换到上一周
|
||
await getClassroomCourses()
|
||
}
|
||
/** 自定义下周按钮点击事件 */
|
||
const nextWeekCustomClick = async () => {
|
||
fullCalendar.value.getApi().next()
|
||
await getClassroomCourses()
|
||
}
|
||
|
||
/** 日历组件属性 */
|
||
const calendarOptions = ref<CalendarOptions>({
|
||
// 插件
|
||
plugins: [
|
||
listPlugin, // 列表视图插件
|
||
timeGridPlugin, // 周视图和日视图插件
|
||
interactionPlugin, // 交互插件,支持点击事件
|
||
],
|
||
// 自定义按钮
|
||
customButtons: {
|
||
preWeekCustom: {
|
||
text: '上周',
|
||
click() {
|
||
preWeekCustomClick() // 点击调用 preWeekCustomClick 函数
|
||
},
|
||
},
|
||
nextWeekCustom: {
|
||
text: '下周',
|
||
click() {
|
||
nextWeekCustomClick() // 点击调用 nextWeekCustomClick 函数
|
||
},
|
||
},
|
||
},
|
||
/** 修改headerToolbar */
|
||
headerToolbar: {
|
||
left: 'preWeekCustom',
|
||
center: 'title', // 显示日历标题
|
||
right: 'nextWeekCustom',
|
||
},
|
||
dayHeaderFormat: {
|
||
weekday: 'short', // 显示周几,如"周日"
|
||
month: '2-digit', // 显示两位数的月份
|
||
day: '2-digit', // 显示两位数的日期
|
||
omitCommas: true, // 去除逗号
|
||
},
|
||
/** 设置日历高度 */
|
||
contentHeight: uni.getWindowInfo().windowHeight - 260, // 动态计算高度
|
||
timeZone: 'Asia/Shanghai',
|
||
/** 默认视图 (月:dayGridMonth,周:timeGridWeek,日:timeGridDay) */
|
||
initialView: 'timeGridWeek', // 默认显示周视图
|
||
firstDay: 1, // 一周的第一天,0表示星期天,1表示星期一
|
||
slotMinTime: '08:00', // 最小时间段,08:00
|
||
slotMaxTime: '22:10', // 最大时间段,22:10
|
||
slotDuration: '00:05', // 时间间隔,5分钟
|
||
slotLabelFormat: {
|
||
hour: '2-digit',
|
||
minute: '2-digit',
|
||
omitZeroMinute: false, // 忽略零分钟
|
||
hour12: false, // 24小时制
|
||
meridiem: 'short',
|
||
},
|
||
eventTimeFormat: {
|
||
hour: '2-digit',
|
||
minute: '2-digit',
|
||
hour12: false, // 24小时制
|
||
},
|
||
eventColor: '#3BB2E3', // 全部日历日程背景色
|
||
themeSystem: 'bootstrap', // 主题色(本地测试未能生效)
|
||
events: currentEvents.value, // 事件列表
|
||
editable: false, // 是否可以拖动修改事件
|
||
allDaySlot: false, // 是否显示全天事件区域
|
||
nowIndicator: true, // 是否显示当前时间线
|
||
selectable: true, // 是否可以选择时间段
|
||
selectMirror: true, // 是否在选择时间段时显示虚影
|
||
handleWindowResize: true, // 是否在窗口大小变化时调整日历
|
||
navLinks: false, // 是否可以通过点击日期导航
|
||
fixedWeekCount: true, // 每月显示固定周数
|
||
showNonCurrentDates: true, // 是否显示非当前月的日期
|
||
dayMaxEvents: true, // 每天最大事件数,超过则显示更多按钮
|
||
weekends: true, // 是否显示周末
|
||
/** 切换语言 */
|
||
locale: 'zh-cn', // 语言设置为简体中文
|
||
buttonText: {
|
||
today: '今天',
|
||
week: '周视图',
|
||
day: '日',
|
||
list: '周列表',
|
||
},
|
||
})
|
||
|
||
onMounted(async () => {
|
||
campus.value = campusId.value
|
||
await getBuildList()
|
||
await getClassroomList()
|
||
await getClassroomCourses()
|
||
fullCalendar.value.getApi().render()
|
||
})
|
||
// #endif
|
||
</script>
|
||
|
||
<template>
|
||
<!-- #ifdef H5 -->
|
||
<UBasePage>
|
||
<view class="query-select">
|
||
<view class="query-select-item">
|
||
<view class="query-select-item-left">
|
||
<uni-icons type="map" color="#A0E2AA" size="24" />
|
||
<text>校区</text>
|
||
</view>
|
||
<view>
|
||
<cus-selects
|
||
:value="campus"
|
||
filterable
|
||
:data="campusList"
|
||
:value-type="{ label: 'name', value: 'id' }"
|
||
placeholder="请选择校区~"
|
||
style="width: 100%"
|
||
@change="onCampusSelectChange"
|
||
/>
|
||
</view>
|
||
</view>
|
||
<view class="query-select-item">
|
||
<view class="query-select-item-left">
|
||
<uni-icons type="home" color="#F9BD56" size="24" />
|
||
<text>教学楼</text>
|
||
</view>
|
||
<view>
|
||
<cus-selects
|
||
:value="buildId"
|
||
filterable
|
||
:data="buildList"
|
||
:value-type="{ label: 'name', value: 'id' }"
|
||
placeholder="请选择教学楼~"
|
||
style="width: 100%"
|
||
@change="onBuildSelectChange"
|
||
/>
|
||
</view>
|
||
</view>
|
||
<view class="query-select-item">
|
||
<view class="query-select-item-left">
|
||
<uni-icons type="location" color="#FEC2BF" size="24" />
|
||
<text>教室</text>
|
||
</view>
|
||
<view>
|
||
<cus-selects
|
||
:value="classroomId"
|
||
filterable
|
||
:data="classroomList"
|
||
:value-type="{ label: 'name', value: 'id' }"
|
||
placeholder="请选择教室~"
|
||
style="width: 100%"
|
||
@change="onClassroomSelectChange"
|
||
/>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<FullCalendar ref="fullCalendar" :options="calendarOptions">
|
||
<template #eventContent="arg">
|
||
<view class="calender-content">
|
||
{{ arg.timeText }}
|
||
</view>
|
||
<view class="calender-content">
|
||
{{ arg.event.title }}
|
||
</view>
|
||
<view class="calender-content">
|
||
上课班级数量: {{ arg.event.extendedProps.classnames.length }}
|
||
</view>
|
||
</template>
|
||
</FullCalendar>
|
||
</UBasePage>
|
||
<!-- #endif -->
|
||
<!-- #ifndef H5 -->
|
||
<web-view src="/hybrid/html/calender.html" :fullscreen="false" style="width: 100%; height: 500px;" />
|
||
<!-- #endif -->
|
||
</template>
|
||
|
||
<style lang="scss">
|
||
.query-select{
|
||
padding: 10rpx 20rpx;
|
||
background-image: linear-gradient(-225deg, #FFFEFF 0%, #D7FFFE 100%);
|
||
.query-select-item{
|
||
border-bottom: 2rpx solid #e9e9e9;
|
||
padding: 10rpx 0;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
.query-select-item-left {
|
||
width: 30%;
|
||
text {
|
||
margin-left: 5rpx;
|
||
color: #29a1f7;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
.calender-content{
|
||
text-align: center;
|
||
}
|
||
:deep(.fc .fc-toolbar-title){
|
||
font-size: 30rpx;
|
||
}
|
||
:deep(.fc){
|
||
font-size: 26rpx;
|
||
}
|
||
:deep(.fc .fc-toolbar.fc-header-toolbar){
|
||
margin-bottom: 10rpx;
|
||
}
|
||
</style>
|