diff --git a/src/components/customModule/dateTimeRange/dateTimeRange.vue b/src/components/customModule/dateTimeRange/dateTimeRange.vue
index 9e35569..23d9fcf 100644
--- a/src/components/customModule/dateTimeRange/dateTimeRange.vue
+++ b/src/components/customModule/dateTimeRange/dateTimeRange.vue
@@ -23,7 +23,8 @@
props: {
type: {
type: String,
- default: 'datetimerange'
+ default: 'datetimerange',
+ validator: (value) => ['datetimerange', 'daterange'].includes(value)
},
value: {
type: Array,
@@ -47,26 +48,28 @@
},
format: {
type: String,
- default: 'YYYY-MM-DD HH:mm:ss'
+ default: 'YYYY-MM-DD HH:mm:ss',
+ validator: (value) => ['YYYY-MM-DD HH:mm:ss', 'yyyy-MM-dd HH:mm:ss', 'YYYY-MM-DD', 'yyyy-MM-dd'].includes(value)
},
defaultTime: {
type: Array,
- default: () => ['00:00:00', '00:00:00']
+ default: () => ['00:00:00', '23:59:59'],
+ validator: (value) => {
+ if (!Array.isArray(value) || value.length !== 2) return false;
+ return value.every(time => typeof time === 'string' && /^([01]\d|2[0-3]):([0-5]\d):([0-5]\d)$/.test(time));
+ }
},
},
- watch: {
- value: {
- immediate: true,
- handler(val) {
- this.dateRange = val || [];
+ computed: {
+ dateRange: {
+ get() {
+ return this.value || [];
+ },
+ set(newValue) {
+ this.$emit('input', newValue);
}
}
},
- data() {
- return {
- dateRange: this.value || []
- };
- },
methods: {
handleDateChange(range) {
this.$emit('input', [range[0], range[1]]);
diff --git a/src/components/customModule/dateTimeRange/dateTimeRangePicker.vue b/src/components/customModule/dateTimeRange/dateTimeRangePicker.vue
index cbb434a..e4a9010 100644
--- a/src/components/customModule/dateTimeRange/dateTimeRangePicker.vue
+++ b/src/components/customModule/dateTimeRange/dateTimeRangePicker.vue
@@ -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)"
>
@@ -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;
diff --git a/src/router/index.js b/src/router/index.js
index 5ace805..34e681d 100644
--- a/src/router/index.js
+++ b/src/router/index.js
@@ -881,8 +881,8 @@ export const dynamicRoutes = [
{
path: ':id?',
component: () => import('@/views/resource/mtrProbe/mtrProbeView'),
- name: 'mtrProbe_view',
- meta: { title: '新建MTR探测策略', activeMenu: '/resource/mtrAgent' }
+ name: 'mtrAgentAndProbe',
+ meta: { title: '新建MTR探测策略', noCache: false, activeMenu: '/resource/mtrAgent' }
}
]
},
@@ -896,8 +896,8 @@ export const dynamicRoutes = [
{
path: ':id?',
component: () => import('@/views/resource/mtrProbe/mtrProbeView'),
- name: 'mtrProbe_view',
- meta: { title: 'MTR探测策略信息', activeMenu: '/resource/mtrProbe' }
+ name: 'mtrProbeView',
+ meta: { title: 'MTR探测策略信息', noCache: false, activeMenu: '/resource/mtrProbe' }
}
]
},
diff --git a/src/views/resource/mtrProbe/mtrProbeView.vue b/src/views/resource/mtrProbe/mtrProbeView.vue
index cd04b35..43374fa 100644
--- a/src/views/resource/mtrProbe/mtrProbeView.vue
+++ b/src/views/resource/mtrProbe/mtrProbeView.vue
@@ -10,11 +10,11 @@
-