From ac163a126aef4f169ab8a349f07d8a8fbd7e8153 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BA=B7=E5=86=89=E5=86=89?= Date: Mon, 16 Mar 2026 18:06:58 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=89=B9=E9=87=8F=E6=B5=81?= =?UTF-8?q?=E9=87=8F=E5=9B=BE=E5=BD=A2=E5=B1=95=E7=A4=BA=E9=A1=B5=E9=9D=A2?= =?UTF-8?q?Echarts=E5=9B=BE=E8=A1=A8X=E8=BD=B4=E6=95=B0=E6=8D=AE=E7=BC=A9?= =?UTF-8?q?=E8=BF=9B=E9=80=BB=E8=BE=91=EF=BC=8C=E6=8F=90=E5=8D=87=E5=B1=95?= =?UTF-8?q?=E7=A4=BA=E6=95=88=E6=9E=9C=E3=80=81=E5=8D=95=E9=A1=B5=E9=9D=A2?= =?UTF-8?q?=E5=A4=9A=E8=B7=AF=E7=94=B1=E7=BC=93=E5=AD=98=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1、优化批量流量图形展示页面Echarts图表X轴数据缩进逻辑,提升展示效果 2、添加代码注释,移除冗余代码,优化代码体积与执行效率 3、优化自定义日期时间区间组件,完善功能与视觉表现 4、优化路由匹配逻辑,解决单页面适配多路由场景下的 keep-alive 缓存错乱问题 --- .../dateTimeRange/dateTimeRange.vue | 29 +-- .../dateTimeRange/dateTimeRangePicker.vue | 191 ++++++++++++++++-- src/router/index.js | 8 +- src/views/resource/mtrProbe/mtrProbeView.vue | 11 +- src/views/resource/serverRegister/details.vue | 58 ------ src/views/resource/serverRegister/index.vue | 49 ++--- .../resource/serverRegister/trafficChart.vue | 26 +-- 7 files changed, 223 insertions(+), 149 deletions(-) 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 @@
-