1. 项目背景与需求分析在开发基于Vue3和Element Plus的管理系统时我们经常会遇到需要处理日期选择器的特殊需求。最近我在开发一个财务统计系统时遇到了一个典型场景用户需要选择年份来查看不同年度的报表数据但系统要求禁止选择重复年份。这个需求看似简单但实现起来有几个技术难点Element Plus的DatePicker组件默认不支持直接禁用特定年份需要高效地判断年份是否重复需要在用户交互时实时响应状态变化2. 技术选型与方案设计2.1 为什么选择mapSethas方案在JavaScript中我们有多种数据结构可以用来存储和判断重复值数组(Array)查找需要遍历时间复杂度O(n)对象(Object)查找快但键只能是字符串Map键值对集合键可以是任意类型Set值唯一的集合查找效率高经过性能测试对比最终选择了MapSet的组合方案原因如下查找效率Set的has方法时间复杂度是O(1)远高于数组的遍历类型安全Map可以存储Number类型的年份作为键内存效率相比对象Map对频繁增删的场景更友好2.2 Element Plus日期选择器的限制与扩展Element Plus的el-date-picker组件提供了disabledDate属性可以传入一个函数来禁用特定日期el-date-picker v-modelselectedYear typeyear :disabledDatedisabledYear /但默认情况下这个函数接收的是Date对象我们需要将其转换为年份数字进行比较。3. 核心实现步骤3.1 数据结构初始化首先在组件中初始化我们的数据结构import { ref, reactive } from vue const selectedYears reactive(new Map()) // 存储已选年份 const yearSet reactive(new Set()) // 用于快速查找3.2 实现禁用年份函数关键的disabledYear函数实现const disabledYear (date) { const year date.getFullYear() return yearSet.has(year) }3.3 处理年份选择事件当用户选择年份时更新数据结构const handleYearChange (val) { const year val.getFullYear() if (!yearSet.has(year)) { yearSet.add(year) selectedYears.set(year, val) } else { // 已选年份处理逻辑 console.warn(年份 ${year} 已被选择) } }4. 性能优化与实践技巧4.1 内存管理优化在实际项目中我们需要注意Map和Set的内存释放// 移除年份时 const removeYear (year) { yearSet.delete(year) selectedYears.delete(year) }4.2 响应式处理的注意事项由于我们使用了reactive包装Map和Set需要注意直接修改Map/Set的内容不会触发响应式更新需要重新赋值才能触发更新// 错误方式 - 不会触发更新 yearSet.add(newYear) // 正确方式 const newSet new Set(yearSet) newSet.add(newYear) yearSet newSet4.3 大型数据集的优化当需要处理大量年份时如50年以上可以考虑使用WeakMap/WeakSet减少内存占用实现年份范围限制添加防抖处理频繁操作5. 完整组件实现示例下面是一个完整的单文件组件实现template div el-date-picker v-modelcurrentYear typeyear placeholder选择年份 :disabledDatedisabledYear changehandleYearChange / div v-ifselectedYears.size 0 h3已选年份/h3 ul li v-for[year, date] in selectedYears :keyyear {{ year }} el-button clickremoveYear(year) sizesmall移除/el-button /li /ul /div /div /template script setup import { ref, reactive } from vue const currentYear ref() const selectedYears reactive(new Map()) const yearSet reactive(new Set()) const disabledYear (date) { return yearSet.has(date.getFullYear()) } const handleYearChange (date) { const year date.getFullYear() if (!yearSet.has(year)) { // 需要重新赋值才能触发响应式更新 const newSet new Set(yearSet) newSet.add(year) yearSet.clear() newSet.forEach(y yearSet.add(y)) selectedYears.set(year, date) } } const removeYear (year) { const newSet new Set(yearSet) newSet.delete(year) yearSet.clear() newSet.forEach(y yearSet.add(y)) selectedYears.delete(year) } /script6. 常见问题与解决方案6.1 日期格式不一致问题在实际项目中后端返回的日期格式可能不一致。我们需要统一处理// 统一转换为Date对象 const normalizeDate (date) { if (date instanceof Date) return date if (typeof date string) return new Date(date) if (typeof date number) return new Date(date) throw new Error(不支持的日期格式) }6.2 时区问题处理处理跨时区项目时需要注意// 获取UTC年份 const getUTCFullYear (date) { return new Date(date.getTime() date.getTimezoneOffset() * 60000).getFullYear() }6.3 与Vuex/Pinia状态管理集成当需要全局管理已选年份时// 在store中 const useYearStore defineStore(years, { state: () ({ years: new Set() }), actions: { addYear(year) { this.years.add(year) // 需要返回新Set触发响应式 this.years new Set(this.years) } } })7. 扩展应用场景这种技术方案不仅适用于年份选择还可以应用于月份选择器去重工作日选择限制特殊日期黑名单预约时间段去重例如实现一个不可重复选择月份的组件const disabledMonth (date) { const month date.getMonth() return selectedMonths.has(month) }8. 替代方案对比除了MapSet方案还有其他实现方式8.1 纯数组方案// 实现简单但性能较差 const disabledYear (date) { return selectedYearsArray.includes(date.getFullYear()) }8.2 对象方案// 需要将数字转换为字符串 const disabledYear (date) { return !!selectedYearsObj[date.getFullYear().toString()] }性能对比表格方案添加时间复杂度查找时间复杂度内存占用数组O(1)O(n)低对象O(1)O(1)中MapSetO(1)O(1)中BitmaskO(1)O(1)极低提示对于年份范围有限的情况如1900-2100可以考虑使用Bitmask方案将每个年份对应一个二进制位可以极大减少内存占用。9. 测试与调试技巧9.1 单元测试要点import { mount } from vue/test-utils import YearPicker from ./YearPicker.vue test(应该禁用已选年份, async () { const wrapper mount(YearPicker) await wrapper.vm.handleYearChange(new Date(2023, 0)) expect(wrapper.vm.disabledYear(new Date(2023, 0))).toBe(true) })9.2 性能测试方法使用console.time测试大数据量下的性能console.time(添加10000个年份) for (let i 0; i 10000; i) { yearSet.add(2000 i) } console.timeEnd(添加10000个年份) console.time(查找10000次) for (let i 0; i 10000; i) { yearSet.has(2000 Math.floor(Math.random() * 10000)) } console.timeEnd(查找10000次)10. 项目经验总结在实际项目中使用这种方案后我总结了以下几点经验数据结构选择对于小于100个项目的场景其实各种方案差异不大选择最直观的实现即可响应式陷阱Vue3的reactive对ES6集合类型的支持有限需要特别注意移动端适配在移动设备上大量年份渲染仍可能出现性能问题需要虚拟滚动优化无障碍访问记得为禁用的年份添加适当的ARIA属性el-date-picker :disabled-datedisabledYear :popper-options{ modifiers: [{ name: applyAriaDisabled, enabled: true, phase: write, fn({ state }) { state.elements.popper.querySelectorAll(.disabled .el-date-table-cell) .forEach(el { el.setAttribute(aria-disabled, true) }) } }] } /这种基于MapSet的实现方案在我们的生产环境中表现稳定成功支持了财务系统中复杂的年份选择需求代码也保持了良好的可维护性。
企业数字化 ERP 产品动态
相关推荐
WinCC动画插件工程化:刷新周期、脚本调度与坐标换算 上个月帮一个做水处理自控的朋友看画面卡顿的问题,画面里 6 台泵、4 个罐、十几条管道,全用脚本做旋转和流动,鼠标拖个窗口都一卡一卡的,客户那边还抱怨"动画跑着跑着就停了"。我把他们的工程打开看了一圈,问… · 2026/11/5 18:50:00
OpenClaw智能体通讯架构与会话管理实战 1. OpenClaw通讯架构概览OpenClaw采用典型的父子智能体协作架构,其核心通讯机制建立在会话(session)传递模型之上。在实际部署中,父智能体作为中央调度器,通过session_send接口向子智能体分发任务,而子智能体则保持独立的会话上下… · 2026/11/5 7:02:36
Vue 3 + Vite 从代码到浏览器全链路解析与调试指南 Vue 3 项目从代码到浏览器,中间到底发生了什么?这个问题我在带新人的时候被问过不下二十次。很多人敲完npm run dev,看到终端里蹦出一行本地地址,浏览器自动弹开页面,就觉得“跑起来了,收工”。可一旦页面白… · 2026/11/5 16:18:03
EasyU优启通PE维护盘实战指南:制作、启动与核心操作全解析 1. 为什么一个PE维护盘值得单独拿出来聊装系统、修引导、救数据、清病毒,这些活儿但凡干过几次的人都知道,真正决定效率的往往不是你手速多快,而是手边那套PE环境靠不靠谱。EasyU(圈内习惯叫优启通)就是在这个场景下被… · 2026/11/5 16:49:35
Rust原来很简单二:所有权系统 Part 2 核心篇 —— 所有权系统
本篇覆盖 第 6 ~ 10 章:所有权、借用与引用、生命周期、字符串与切片、内存模型。 ⚠️ 这是全书最重要的一篇。 把它吃透,Rust 从此“很简单”;跳过它,你会处处碰壁。请慢下来,动手敲每一段代码。 第 6 章 所有权:Rust 的第一性原理
6… · 2026/11/5 16:26:08
基于Hadoop+Spark的民宿智能推荐系统设计与实现 1. 项目概述:大数据时代的民宿智能推荐这个毕业设计项目融合了当前大数据领域最主流的技术栈,构建了一个完整的民宿推荐系统解决方案。作为一名长期从事大数据系统开发的工程师,我认为这个选题非常贴合实际应用场景——随着旅游业的复苏和共享… · 2026/11/5 14:59:25
ADS122U04IPWR+STM32F427ZI工业级温度测量信号链设计 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 14:10:41
UART接口24位ADC搭配RS485,解决远距离温度监控难题 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 14:10:41
UART接口24位ADC搭配PIC18F46K22的RTD与热电偶测温实践 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 14:10:41
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:27:59
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:28:03
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:28:07
多智能体协作系统架构设计与实践优化 1. 多智能体协作系统的时代背景2017年Transformer架构的诞生彻底改变了人工智能的发展轨迹。当行业还沉浸在单一大模型带来的性能突破时,Anthropic的研究团队已经将目光投向了更前沿的领域——如何让多个智能体通过协作产生超越单体能力的系统智能。这就像交响乐团中… · 2026/11/5 0:00:39
大模型系统性入门实战指南:从原理到部署微调的全链路路线 提到大模型的系统性入门,我先说个结论:这事真不难,难的是资料太散、路线太多,今天这个框架明天那个工具,新人很容易在“收藏夹吃灰”和“教程看到一半放弃”之间反复横跳。市面上关于大模型的内容少说有几十万篇&#… · 2026/11/5 0:00:39
鸿蒙文件系统沙箱机制解析与开发实践 1. 鸿蒙文件系统沙箱机制深度解析作为一名从Android转向鸿蒙开发的工程师,我深刻体会到文件系统访问是迁移过程中最先遇到的"拦路虎"。在Android上运行良好的文件读写代码,在鸿蒙真机环境却频频出现文件写入后神秘消失或直接抛出权限拒绝错误的… · 2026/11/5 0:00:39
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:27:59
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:28:03
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:28:07