fix(calendar): 优化历史营期显示和交互体验

- 移除顶部状态指示器,改为通过事件点样式区分历史营期
- 添加鼠标悬停延迟显示和离开隐藏功能
- 实现历史营期数据映射到日历的功能
- 调整历史营期切换时的日期跳转逻辑
- 优化历史营期事件点的视觉样式
This commit is contained in:
2025-08-29 17:18:44 +08:00
parent 4ad91cabe3
commit d6db489f80
2 changed files with 131 additions and 63 deletions

View File

@@ -5,8 +5,8 @@ import { useUserStore } from '@/stores/user'
// 创建axios实例 // 创建axios实例
const service = axios.create({ const service = axios.create({
// baseURL: 'https://mldash.nycjy.cn/' || '', // API基础路径支持完整URL baseURL: 'https://mldash.nycjy.cn/' || '', // API基础路径支持完整URL
baseURL: 'http://192.168.15.121:8890' || '', // API基础路径支持完整URL // baseURL: 'http://192.168.15.121:8890' || '', // API基础路径支持完整URL
timeout: 100000, // 请求超时时间 timeout: 100000, // 请求超时时间
headers: { headers: {
'Content-Type': 'application/json;charset=UTF-8' 'Content-Type': 'application/json;charset=UTF-8'

View File

@@ -41,22 +41,6 @@
</div> </div>
</div> </div>
<!-- 营期状态指示器 -->
<div class="period-indicator">
<div v-if="isViewingHistory" class="history-indicator">
<svg viewBox="0 0 24 24" width="16" height="16">
<path fill="currentColor" d="M13,3A9,9 0 0,0 4,12H1L4.89,15.89L4.96,16.03L9,12H6A7,7 0 0,1 13,5A7,7 0 0,1 20,12A7,7 0 0,1 13,19C11.07,19 9.32,18.21 8.06,16.94L6.64,18.36C8.27,20 10.5,21 13,21A9,9 0 0,0 22,12A9,9 0 0,0 13,3Z"/>
</svg>
<span>正在查看{{ selectedHistoryPeriod.periodNum }}期历史营期</span>
</div>
<div v-else class="current-indicator">
<svg viewBox="0 0 24 24" width="16" height="16">
<path fill="currentColor" d="M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4M12,6A6,6 0 0,1 18,12A6,6 0 0,1 12,18A6,6 0 0,1 6,12A6,6 0 0,1 12,6M12,8A4,4 0 0,0 8,12A4,4 0 0,0 12,16A4,4 0 0,0 16,12A4,4 0 0,0 12,8Z"/>
</svg>
<span>当前营期</span>
</div>
</div>
<!-- 星期标题 --> <!-- 星期标题 -->
<div class="weekdays"> <div class="weekdays">
<div v-for="day in weekdays" :key="day" class="weekday"> <div v-for="day in weekdays" :key="day" class="weekday">
@@ -80,9 +64,10 @@
} }
]" ]"
@mouseenter="selectDate(date, $event)" @mouseenter="selectDate(date, $event)"
@mouseleave="onMouseLeave"
> >
<span class="date-number">{{ date.day }}</span> <span class="date-number">{{ date.day }}</span>
<div v-if="date.hasEvent && !isRestDay(date.dateStr)" class="event-dot" :class="getEventTypeClass(date.dateStr)"></div> <div v-if="date.hasEvent && !isRestDay(date.dateStr)" class="event-dot" :class="[getEventTypeClass(date.dateStr), { 'history-period': isViewingHistory }]"></div>
<span v-if="isRestDay(date.dateStr)" class="rest-text"></span> <span v-if="isRestDay(date.dateStr)" class="rest-text"></span>
<span v-if="date.isHoliday" class="holiday-text"></span> <span v-if="date.isHoliday" class="holiday-text"></span>
<div v-if="isHolidayStart(date.dateStr)" class="holiday-name"> <div v-if="isHolidayStart(date.dateStr)" class="holiday-name">
@@ -295,6 +280,7 @@ const selectedDate = ref(null);
const today = new Date(); const today = new Date();
const showTooltip = ref(false); const showTooltip = ref(false);
const tooltipPosition = ref({ x: 0, y: 0 }); const tooltipPosition = ref({ x: 0, y: 0 });
const hoverTimer = ref(null);
// 营期设置相关 // 营期设置相关
const showCampModal = ref(false); const showCampModal = ref(false);
@@ -473,22 +459,31 @@ const nextMonth = async () => {
} }
}; };
// 方法:选择日期 // 方法:选择日期(鼠标悬停延迟触发)
const selectDate = (date, event) => { const selectDate = (date, event) => {
selectedDate.value = date; selectedDate.value = date;
// 清除之前的定时器
if (hoverTimer.value) {
clearTimeout(hoverTimer.value);
}
// 只有当日期有事件时才显示悬浮框 // 只有当日期有事件时才显示悬浮框
if (event && date.hasEvent) { if (event && date.hasEvent) {
tooltipPosition.value = { tooltipPosition.value = {
x: event.clientX + 10, x: event.clientX + 10,
y: event.clientY - 10 y: event.clientY - 10
}; };
showTooltip.value = true;
// 3秒后自动隐藏 // 延迟500毫秒后显示悬浮框
setTimeout(() => { hoverTimer.value = setTimeout(() => {
showTooltip.value = false; showTooltip.value = true;
}, 3000);
// 3秒后自动隐藏
setTimeout(() => {
showTooltip.value = false;
}, 3000);
}, 500);
} }
}; };
@@ -497,6 +492,15 @@ const hideTooltip = () => {
showTooltip.value = false; showTooltip.value = false;
}; };
// 方法:鼠标离开日期单元格
const onMouseLeave = () => {
// 清除延迟显示的定时器
if (hoverTimer.value) {
clearTimeout(hoverTimer.value);
hoverTimer.value = null;
}
};
// 方法:显示历史记录 // 方法:显示历史记录
const showHistory = async () => { const showHistory = async () => {
try { try {
@@ -558,10 +562,14 @@ const switchToHistoryPeriod = async (period) => {
// 关闭历史营期弹框 // 关闭历史营期弹框
showHistoryModal.value = false; showHistoryModal.value = false;
// 重新获取营期数据以更新日历显示 // 使用历史营期数据构建日历显示
await CenterCampPeriodAdmin(); mapHistoryPeriodToCalendar(period);
// 切换日历到历史营期的时间段
const startDate = new Date(period.startDate);
currentYear.value = startDate.getFullYear();
currentMonth.value = startDate.getMonth();
alert(`已切换到第${period.periodNum}期历史营期`);
} else { } else {
alert('切换历史营期失败,请重试'); alert('切换历史营期失败,请重试');
} }
@@ -580,6 +588,11 @@ const returnToCurrentPeriod = async () => {
// 重新获取当前营期数据 // 重新获取当前营期数据
await CenterCampPeriodAdmin(); await CenterCampPeriodAdmin();
// 切换日历回到当前时间
const now = new Date();
currentYear.value = now.getFullYear();
currentMonth.value = now.getMonth();
alert('已返回当前营期'); alert('已返回当前营期');
} catch (error) { } catch (error) {
console.error('返回当前营期失败:', error); console.error('返回当前营期失败:', error);
@@ -671,6 +684,9 @@ const isRestDay = (dateStr) => {
// 方法:检查营期是否应该结束 // 方法:检查营期是否应该结束
const shouldShowFinishCamp = () => { const shouldShowFinishCamp = () => {
// 如果正在查看历史营期,不显示结束营期按钮
if (isViewingHistory.value) return false;
if (isCampFinished.value) return false; if (isCampFinished.value) return false;
// 检查是否有营期相关的事件 // 检查是否有营期相关的事件
@@ -908,6 +924,71 @@ const parseDateRange = (dateRangeStr) => {
}; };
}; };
// 方法:将历史营期数据映射到日历
const mapHistoryPeriodToCalendar = (historyPeriod) => {
// 清除之前的营期相关事件
events.value = events.value.filter(event => !event.isCampEvent);
let eventId = events.value.length + 1;
const startDate = new Date(historyPeriod.startDate);
const endDate = new Date(historyPeriod.endDate);
// 计算营期总天数
const totalDays = Math.ceil((endDate - startDate) / (1000 * 60 * 60 * 24)) + 1;
let currentDate = new Date(startDate);
// 1. 添加接数据事件
for (let i = 0; i < historyPeriod.dataDays; i++) {
const dateStr = formatDateToString(currentDate);
events.value.push({
id: eventId++,
date: dateStr,
title: '接数据',
description: `${historyPeriod.periodNum}期历史营期 - 数据接收阶段 (第${i + 1}天)`,
isCampEvent: true,
type: 'data'
});
currentDate.setDate(currentDate.getDate() + 1);
}
// 2. 添加课程事件课1-4连续4天
const courses = ['课1', '课2', '课3', '课4'];
courses.forEach((courseTitle, index) => {
if (currentDate <= endDate) {
const dateStr = formatDateToString(currentDate);
events.value.push({
id: eventId++,
date: dateStr,
title: courseTitle,
description: `${historyPeriod.periodNum}期历史营期 - ${courseTitle}阶段`,
isCampEvent: true,
type: 'course'
});
currentDate.setDate(currentDate.getDate() + 1);
}
});
// 3. 添加休息日事件(在营期结束前的几天)
const restStartDate = new Date(endDate);
restStartDate.setDate(endDate.getDate() - historyPeriod.restDays + 1);
for (let i = 0; i < historyPeriod.restDays; i++) {
const restDate = new Date(restStartDate);
restDate.setDate(restStartDate.getDate() + i);
const dateStr = formatDateToString(restDate);
events.value.push({
id: eventId++,
date: dateStr,
title: '休息',
description: `${historyPeriod.periodNum}期历史营期 - 休息日 (第${i + 1}天)`,
isCampEvent: true,
type: 'rest'
});
}
};
@@ -1376,6 +1457,28 @@ onMounted(async () => {
box-shadow: 0 2px 4px rgba(40, 167, 69, 0.3); box-shadow: 0 2px 4px rgba(40, 167, 69, 0.3);
} }
/* 历史营期事件点样式 */
.event-dot.history-period {
opacity: 0.6;
border: 2px solid #6c757d;
transform: scale(0.8);
}
.event-dot.history-period.event-data {
background: #6c757d;
box-shadow: 0 2px 4px rgba(108, 117, 125, 0.3);
}
.event-dot.history-period.event-course {
background: #6c757d;
box-shadow: 0 2px 4px rgba(108, 117, 125, 0.3);
}
.event-dot.history-period.event-rest {
background: #6c757d;
box-shadow: 0 2px 4px rgba(108, 117, 125, 0.3);
}
/* 休息日文字样式 */ /* 休息日文字样式 */
.rest-text { .rest-text {
position: absolute; position: absolute;
@@ -1702,40 +1805,5 @@ onMounted(async () => {
box-shadow: 0 2px 4px rgba(40, 167, 69, 0.2); box-shadow: 0 2px 4px rgba(40, 167, 69, 0.2);
} }
/* 营期状态指示器样式 */
.period-indicator {
display: flex;
justify-content: center;
padding: 8px 0;
margin-bottom: 8px;
border-bottom: 1px solid #e9ecef;
}
.history-indicator,
.current-indicator {
display: flex;
align-items: center;
gap: 6px;
padding: 6px 12px;
border-radius: 20px;
font-size: 13px;
font-weight: 500;
}
.history-indicator {
background: linear-gradient(135deg, #ffc107, #fd7e14);
color: white;
box-shadow: 0 2px 4px rgba(255, 193, 7, 0.3);
}
.current-indicator {
background: linear-gradient(135deg, #28a745, #20c997);
color: white;
box-shadow: 0 2px 4px rgba(40, 167, 69, 0.3);
}
.history-indicator svg,
.current-indicator svg {
flex-shrink: 0;
}
</style> </style>