优化批量流量图形展示页面Echarts图表X轴数据缩进逻辑,提升展示效果、单页面多路由缓存问题
1、优化批量流量图形展示页面Echarts图表X轴数据缩进逻辑,提升展示效果 2、添加代码注释,移除冗余代码,优化代码体积与执行效率 3、优化自定义日期时间区间组件,完善功能与视觉表现 4、优化路由匹配逻辑,解决单页面适配多路由场景下的 keep-alive 缓存错乱问题
This commit is contained in:
@@ -107,7 +107,7 @@
|
||||
v-for="(day, index) in days"
|
||||
:key="index"
|
||||
class="day-cell"
|
||||
:class="getDayClass(day)"
|
||||
:class="getDayClass(day, index)"
|
||||
@click="selectDate(day)"
|
||||
>
|
||||
<div class="day-content">
|
||||
@@ -185,7 +185,7 @@
|
||||
},
|
||||
pickerOptions: {
|
||||
type: Object,
|
||||
default: () => {shortcuts: []}
|
||||
default: () => ({shortcuts: []})
|
||||
},
|
||||
placement: {
|
||||
type: String,
|
||||
@@ -210,7 +210,11 @@
|
||||
startTime: '',
|
||||
endTime: '',
|
||||
panelStyle: {opacity: 0},
|
||||
actualPlacement: this.placement
|
||||
actualPlacement: this.placement,
|
||||
updatePanelPositionDebounced: null, // 防抖函数引用
|
||||
calendarCache: {}, // 日历数据缓存
|
||||
focusedDayIndex: -1, // 当前焦点日期的索引
|
||||
isKeyboardNavigating: false // 是否正在使用键盘导航
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -222,8 +226,13 @@
|
||||
return [start, end]
|
||||
}
|
||||
return [];
|
||||
},
|
||||
// 缓存键,用于判断是否需要重新生成日历
|
||||
calendarCacheKey() {
|
||||
return `${this.currentMonth}-${this.currentDate}`;
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
value: {
|
||||
immediate: true,
|
||||
@@ -250,29 +259,58 @@
|
||||
if (val) {
|
||||
this.generateCalendar();
|
||||
document.addEventListener('click', this.handleClickOutside);
|
||||
document.addEventListener('scroll', this.updatePanelPosition, true);
|
||||
window.addEventListener('resize', this.updatePanelPosition);
|
||||
document.addEventListener('scroll', this.updatePanelPositionDebounced, true);
|
||||
window.addEventListener('resize', this.updatePanelPositionDebounced);
|
||||
document.addEventListener('keydown', this.handleKeydown);
|
||||
// 初始化焦点到第一个当前月份的日期
|
||||
this.$nextTick(() => {
|
||||
this.focusFirstCurrentMonthDay();
|
||||
setTimeout(() => {
|
||||
this.updatePanelPosition();
|
||||
},500);
|
||||
});
|
||||
} else {
|
||||
document.removeEventListener('click', this.handleClickOutside);
|
||||
document.removeEventListener('scroll', this.updatePanelPosition, true);
|
||||
window.removeEventListener('resize', this.updatePanelPosition);
|
||||
document.removeEventListener('scroll', this.updatePanelPositionDebounced, true);
|
||||
window.removeEventListener('resize', this.updatePanelPositionDebounced);
|
||||
document.removeEventListener('keydown', this.handleKeydown);
|
||||
this.focusedDayIndex = -1;
|
||||
this.isKeyboardNavigating = false;
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.generateCalendar();
|
||||
// 创建防抖函数
|
||||
this.updatePanelPositionDebounced = this.debounce(this.updatePanelPosition, 100);
|
||||
},
|
||||
beforeDestroy() {
|
||||
document.removeEventListener('click', this.handleClickOutside);
|
||||
document.removeEventListener('scroll', this.updatePanelPosition, true);
|
||||
window.removeEventListener('resize', this.updatePanelPosition);
|
||||
document.removeEventListener('scroll', this.updatePanelPositionDebounced, true);
|
||||
window.removeEventListener('resize', this.updatePanelPositionDebounced);
|
||||
document.removeEventListener('keydown', this.handleKeydown);
|
||||
},
|
||||
methods: {
|
||||
// 简单的防抖函数实现
|
||||
debounce(func, wait) {
|
||||
let timeout;
|
||||
return function executedFunction(...args) {
|
||||
const later = () => {
|
||||
clearTimeout(timeout);
|
||||
func.apply(this, args);
|
||||
};
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(later, wait);
|
||||
};
|
||||
},
|
||||
|
||||
// 解析时间字符串为Date对象
|
||||
parseTimeString(timeStr) {
|
||||
if (!timeStr) return new Date(0, 0, 0, 0, 0, 0);
|
||||
const parts = timeStr.split(':').map(Number);
|
||||
return new Date(0, 0, 0, parts[0] || 0, parts[1] || 0, parts[2] || 0);
|
||||
},
|
||||
|
||||
// 更新面板位置
|
||||
updatePanelPosition() {
|
||||
if (!this.showPanel || !this.$refs.triggerRef || !this.$refs.panelRef) {
|
||||
@@ -306,6 +344,13 @@
|
||||
}
|
||||
},
|
||||
generateCalendar() {
|
||||
// 检查缓存
|
||||
const cacheKey = this.calendarCacheKey;
|
||||
if (this.calendarCache[cacheKey]) {
|
||||
this.days = this.calendarCache[cacheKey];
|
||||
return;
|
||||
}
|
||||
|
||||
const year = this.currentMonth;
|
||||
const month = this.currentDate - 1;
|
||||
const firstDayOfMonth = new Date(year, month, 1);
|
||||
@@ -313,12 +358,12 @@
|
||||
const daysInMonth = new Date(year, month + 1, 0).getDate();
|
||||
const prevMonthDays = new Date(year, month, 0).getDate();
|
||||
|
||||
this.days = [];
|
||||
const days = [];
|
||||
|
||||
// 添加上个月的天数
|
||||
for (let i = 0; i < firstDayWeek; i++) {
|
||||
const day = prevMonthDays - firstDayWeek + i + 1;
|
||||
this.days.push({
|
||||
days.push({
|
||||
date: day,
|
||||
month: 'prev',
|
||||
fullDate: new Date(year, month - 1, day)
|
||||
@@ -327,7 +372,7 @@
|
||||
|
||||
// 添加当月天数
|
||||
for (let i = 1; i <= daysInMonth; i++) {
|
||||
this.days.push({
|
||||
days.push({
|
||||
date: i,
|
||||
month: 'current',
|
||||
fullDate: new Date(year, month, i)
|
||||
@@ -337,13 +382,23 @@
|
||||
// 添加下个月的天数
|
||||
const totalCells = 42; // 6行 * 7天
|
||||
const nextMonthDay = 1;
|
||||
while (this.days.length < totalCells) {
|
||||
this.days.push({
|
||||
date: nextMonthDay + (this.days.length - daysInMonth - firstDayWeek),
|
||||
while (days.length < totalCells) {
|
||||
days.push({
|
||||
date: nextMonthDay + (days.length - daysInMonth - firstDayWeek),
|
||||
month: 'next',
|
||||
fullDate: new Date(year, month + 1, nextMonthDay + (this.days.length - daysInMonth - firstDayWeek))
|
||||
fullDate: new Date(year, month + 1, nextMonthDay + (days.length - daysInMonth - firstDayWeek))
|
||||
});
|
||||
}
|
||||
|
||||
// 更新数据和缓存
|
||||
this.days = days;
|
||||
this.calendarCache[cacheKey] = days;
|
||||
|
||||
// 限制缓存大小,避免内存泄漏
|
||||
const cacheKeys = Object.keys(this.calendarCache);
|
||||
if (cacheKeys.length > 10) {
|
||||
delete this.calendarCache[cacheKeys[0]];
|
||||
}
|
||||
},
|
||||
|
||||
selectDate(day) {
|
||||
@@ -372,12 +427,12 @@
|
||||
this.endDate = null;
|
||||
}
|
||||
if (this.startDate && this.endDate && this.dateType === 'datetimerange') {
|
||||
this.startTime = this.startTime ? this.startTime : new Date(0,0,0,this.defaultTime[0].split(':').map(Number)[0],this.defaultTime[0].split(':').map(Number)[1],this.defaultTime[0].split(':').map(Number)[2]);
|
||||
this.endTime = this.endTime ? this.endTime : new Date(0,0,0,this.defaultTime[1].split(':').map(Number)[0],this.defaultTime[1].split(':').map(Number)[1],this.defaultTime[1].split(':').map(Number)[2]);
|
||||
this.startTime = this.startTime ? this.startTime : this.parseTimeString(this.defaultTime[0]);
|
||||
this.endTime = this.endTime ? this.endTime : this.parseTimeString(this.defaultTime[1]);
|
||||
}
|
||||
},
|
||||
|
||||
getDayClass(day) {
|
||||
getDayClass(day, index) {
|
||||
const classes = [];
|
||||
|
||||
if (day.month !== 'current') {
|
||||
@@ -399,6 +454,11 @@
|
||||
if (this.isInRange(day.fullDate)) {
|
||||
classes.push('in-range');
|
||||
}
|
||||
|
||||
// 添加焦点样式
|
||||
if (this.isKeyboardNavigating && index === this.focusedDayIndex) {
|
||||
classes.push('focused');
|
||||
}
|
||||
|
||||
return classes;
|
||||
},
|
||||
@@ -508,6 +568,93 @@
|
||||
}
|
||||
},
|
||||
|
||||
// 键盘事件处理
|
||||
handleKeydown(event) {
|
||||
if (!this.showPanel) return;
|
||||
|
||||
this.isKeyboardNavigating = true;
|
||||
|
||||
switch(event.key) {
|
||||
case 'ArrowLeft':
|
||||
event.preventDefault();
|
||||
this.moveFocus(-1); // 向左移动
|
||||
break;
|
||||
case 'ArrowRight':
|
||||
event.preventDefault();
|
||||
this.moveFocus(1); // 向右移动
|
||||
break;
|
||||
case 'ArrowUp':
|
||||
event.preventDefault();
|
||||
this.moveFocus(-7); // 向上移动
|
||||
break;
|
||||
case 'ArrowDown':
|
||||
event.preventDefault();
|
||||
this.moveFocus(7); // 向下移动
|
||||
break;
|
||||
case 'Enter':
|
||||
event.preventDefault();
|
||||
this.selectFocusedDay();
|
||||
break;
|
||||
case 'Escape':
|
||||
event.preventDefault();
|
||||
this.closePanel();
|
||||
break;
|
||||
case 'Tab':
|
||||
// 允许Tab键正常导航
|
||||
this.isKeyboardNavigating = false;
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
// 聚焦到第一个当前月份的日期
|
||||
focusFirstCurrentMonthDay() {
|
||||
const firstCurrentMonthIndex = this.days.findIndex(day => day.month === 'current');
|
||||
if (firstCurrentMonthIndex !== -1) {
|
||||
this.focusedDayIndex = firstCurrentMonthIndex;
|
||||
} else {
|
||||
this.focusedDayIndex = 0;
|
||||
}
|
||||
},
|
||||
|
||||
// 移动焦点
|
||||
moveFocus(delta) {
|
||||
if (this.days.length === 0) return;
|
||||
|
||||
let newIndex = this.focusedDayIndex + delta;
|
||||
|
||||
// 边界检查
|
||||
if (newIndex < 0) {
|
||||
// 移动到上个月
|
||||
this.prevMonth();
|
||||
this.$nextTick(() => {
|
||||
newIndex = this.days.length - 7 + (newIndex % 7);
|
||||
if (newIndex < 0) newIndex = 0;
|
||||
this.focusedDayIndex = newIndex;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (newIndex >= this.days.length) {
|
||||
// 移动到下个月
|
||||
this.nextMonth();
|
||||
this.$nextTick(() => {
|
||||
newIndex = newIndex % 7;
|
||||
this.focusedDayIndex = newIndex;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this.focusedDayIndex = newIndex;
|
||||
},
|
||||
|
||||
// 选择当前焦点的日期
|
||||
selectFocusedDay() {
|
||||
if (this.focusedDayIndex >= 0 && this.focusedDayIndex < this.days.length) {
|
||||
const day = this.days[this.focusedDayIndex];
|
||||
this.selectDate(day);
|
||||
}
|
||||
},
|
||||
|
||||
formatDateTime(date) {
|
||||
if (!date) return '';
|
||||
const d = new Date(date);
|
||||
@@ -785,6 +932,12 @@
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.day-cell.focused .day-content {
|
||||
box-shadow: 0 0 0 2px #409eff;
|
||||
outline: none;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.day-cell.in-range:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
|
||||
Reference in New Issue
Block a user