YingXingAI/pages/my/schedule.vue

376 lines
9.1 KiB
Vue
Raw 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">
<PageHeader
title="我的排班"
:is-back="true"
:border-bottom="false"
:background="headerBackground"
/>
<view class="content-wrapper">
<view class="form-container">
<view class="form-card calendar-card">
<view class="calendar-header">
<text class="month-action" @click="handlePrevMonth"></text>
<picker
mode="date"
fields="month"
:value="monthValue"
@change="handleMonthPickerChange"
>
<view class="card-title month-title">{{ monthTitle }}</view>
</picker>
<text class="month-action" @click="handleNextMonth"></text>
</view>
<view class="week-row">
<text
v-for="(label, index) in weekLabels"
:key="index"
class="week-cell"
>
{{ label }}
</text>
</view>
<view class="date-grid">
<view
v-for="(item, index) in calendarDays"
:key="index"
class="date-cell"
:class="{
empty: !item.day,
active: item.isToday
}"
@click="handleSelectDay(item)"
>
<view class="day-number">{{ item.day }}</view>
<view v-if="item.hasDuty" class="duty-tag">值班</view>
</view>
</view>
</view>
<view class="form-card duty-card">
<view class="card-title">{{ selectedDateTitle }}</view>
<view class="duty-row">
<text class="duty-label">今日排班:</text>
<text class="duty-value">{{ isSelectedDuty ? "值班" : "" }}</text>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
import PageHeader from "@/components/PageHeader.vue";
export default {
name: "MySchedule",
components: {
PageHeader,
},
data() {
return {
headerBackground: {
background: "transparent",
},
weekLabels: ["日", "一", "二", "三", "四", "五", "六"],
currentYear: 0,
currentMonth: 0,
selectedDay: 0,
scheduleList: [],
calendarDays: [],
};
},
onLoad() {
const today = new Date();
this.currentYear = today.getFullYear();
this.currentMonth = today.getMonth() + 1;
this.selectedDay = today.getDate();
this.calendarDays = this.buildCalendarDays(this.currentYear, this.currentMonth, []);
this.fetchSchedule();
},
computed: {
monthTitle() {
return `${this.currentYear}${String(this.currentMonth).padStart(2, "0")}`;
},
monthValue() {
return `${this.currentYear}-${String(this.currentMonth).padStart(2, "0")}`;
},
selectedDateTitle() {
const dayText = String(this.selectedDay).padStart(2, "0");
return `${this.currentYear}${String(this.currentMonth).padStart(2, "0")}${dayText}`;
},
isSelectedDuty() {
return this.scheduleList.some((item) => {
return (
item.year === this.currentYear &&
item.month === this.currentMonth &&
item.day === this.selectedDay
);
});
},
},
methods: {
fetchSchedule(
year = this.currentYear,
month = this.currentMonth,
day = this.selectedDay
) {
const userInfo = this.vuex_user || {};
const teacherId = userInfo.id || userInfo.Id;
if (!teacherId) {
this.$u.toast("缺少教师ID");
return;
}
const scheduleDate = new Date(year, month - 1, day, 12, 0, 0);
this.$u.api
.getShiftSchedulingData({
SchedulingTime: scheduleDate.toISOString(),
TeacherManagementId: teacherId,
})
.then((res) => {
if (res && res.succeed) {
const list = Array.isArray(res.data) ? res.data : [];
this.scheduleList = list
.map((item) => this.parseScheduleItem(item))
.filter(Boolean);
this.calendarDays = this.buildCalendarDays(
this.currentYear,
this.currentMonth,
this.scheduleList
);
} else {
this.$u.toast(res?.error || "获取排班失败");
}
})
.catch(() => {
this.$u.toast("获取排班失败");
});
},
parseScheduleItem(item) {
if (!item || !item.schedulingTime) return null;
const date = new Date(item.schedulingTime);
if (Number.isNaN(date.getTime())) return null;
return {
id: item.id,
year: date.getFullYear(),
month: date.getMonth() + 1,
day: date.getDate(),
};
},
buildCalendarDays(year, month, scheduleList = []) {
const firstDay = new Date(year, month - 1, 1).getDay();
const daysInMonth = new Date(year, month, 0).getDate();
const totalCells = 42;
const result = [];
const dutySet = new Set(
scheduleList
.filter((item) => item.year === year && item.month === month)
.map((item) => item.day)
);
for (let i = 0; i < totalCells; i += 1) {
const dayNumber = i - firstDay + 1;
if (dayNumber <= 0 || dayNumber > daysInMonth) {
result.push({ day: "", isToday: false, hasDuty: false });
} else {
result.push({
day: dayNumber,
isToday:
dayNumber === this.selectedDay &&
year === this.currentYear &&
month === this.currentMonth,
hasDuty: dutySet.has(dayNumber),
});
}
}
return result;
},
handleSelectDay(item) {
if (!item || !item.day) return;
this.selectedDay = item.day;
this.calendarDays = this.buildCalendarDays(
this.currentYear,
this.currentMonth,
this.scheduleList
);
},
handlePrevMonth() {
this.changeMonth(-1);
},
handleNextMonth() {
this.changeMonth(1);
},
handleMonthPickerChange(event) {
const value = event?.detail?.value || "";
const [yearText, monthText] = value.split("-");
const year = Number(yearText);
const month = Number(monthText);
if (!year || !month) return;
this.updateMonth(year, month);
},
changeMonth(offset) {
const date = new Date(this.currentYear, this.currentMonth - 1 + offset, 1);
this.updateMonth(date.getFullYear(), date.getMonth() + 1);
},
updateMonth(year, month) {
this.currentYear = year;
this.currentMonth = month;
const today = new Date();
if (today.getFullYear() === year && today.getMonth() + 1 === month) {
this.selectedDay = today.getDate();
} else {
this.selectedDay = 1;
}
this.calendarDays = this.buildCalendarDays(
this.currentYear,
this.currentMonth,
this.scheduleList
);
this.fetchSchedule(year, month, this.selectedDay);
},
},
};
</script>
<style scoped>
.schedule-page {
height: 100vh;
background-image: url("@/static/notes/bg.png");
background-position: center top;
background-repeat: no-repeat;
background-size: 100% auto;
display: flex;
flex-direction: column;
}
.content-wrapper {
flex: 1;
display: flex;
flex-direction: column;
padding: 20rpx 30rpx 60px;
overflow: hidden;
box-sizing: border-box;
}
.form-container {
border-radius: 8px;
margin-top: 46rpx;
}
.form-card {
background-color: #ffffff;
border-radius: 20rpx;
padding: 30rpx;
margin-bottom: 20rpx;
}
.calendar-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 20rpx;
}
.card-title {
font-size: 30rpx;
font-weight: 600;
color: #333333;
}
.month-title {
padding: 6rpx 18rpx;
border-radius: 999rpx;
background-color: #f5f6ff;
text-align: center;
min-width: 200rpx;
}
.month-action {
font-size: 40rpx;
color: #4f6aff;
width: 40rpx;
text-align: center;
line-height: 40rpx;
}
.week-row {
display: grid;
grid-template-columns: repeat(7, 1fr);
margin-bottom: 12rpx;
color: #999999;
font-size: 22rpx;
text-align: center;
}
.week-cell {
line-height: 40rpx;
}
.date-grid {
display: grid;
grid-template-columns: repeat(7, 1fr);
gap: 12rpx 0;
}
.date-cell {
height: 82rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
position: relative;
padding-top: 8rpx;
box-sizing: border-box;
}
.date-cell.empty {
color: transparent;
}
.day-number {
width: 48rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 26rpx;
color: #333333;
line-height: 32rpx;
height: 32rpx;
}
.date-cell.active .day-number {
height: 32rpx;
line-height: 32rpx;
background-color: #4f6aff;
color: #ffffff;
border-radius: 8rpx;
text-align: center;
}
.duty-tag {
margin-top: 6rpx;
font-size: 18rpx;
color: #4f6aff;
line-height: 22rpx;
height: 22rpx;
}
.duty-row {
display: flex;
align-items: center;
font-size: 26rpx;
color: #333333;
}
.duty-label {
color: #666666;
}
.duty-value {
color: #4f6aff;
font-weight: 600;
}
</style>