在构建现代Web应用时,用户体验(UX)的设计和优化至关重要。右键菜单(RightClickMenu)作为一种常见的交互模式,能够为用户提供快捷的操作选项,极大提升操作的便利性和效率。Vue.js,作为一款轻量级且高效的前端框架,以其独特的响应式数据绑定和组件化开发特性,成为了开发者们打造优质用户体验的首选工具。而DeepSeek,作为一个集成AI编程功能的平台,能够为Vue开发者提供智能化的代码生成和优化建议,进一步加速开发进程。本文将深入探讨如何结合DeepSeek与Vue.js,共同打造一款体验丝滑、功能丰富的右键菜单组件,旨在为用户带来更加流畅和高效的交互体验。
📚前言
博时基金在人工智能应用方面一直保持前沿探索。2024 年初,公司发现了 DeepSeek 模型在自动编写代码和逻辑推理方面的潜力,率先在自有的昇腾服务器上部署了 DeepSeek-V1 模型,作为公司智能开发工具的基座模型,并在 2024 年 8 月升级为 DeepSeek-V2 模型。2025 年,随着 DeepSeek-R1 模型的发布,博时基金迅速完成内部部署,并开始探索它在投资研究、咨询服务和软件开发等方面的应用。R1 模型在推理能力上表现优异,可以帮助提升工作效率,支持业务创新,同时,它对算力的需求也有所降低,为进一步推广应用创造了条件 。
📚页面效果
📚指令输入
已经创建好了一个基于Vue3的组合式API的项目(Composition API),并能正常运行起来,请帮我用 Vue3的组合式API(Composition API) 生成一个 右键菜单(RightClickMenu) 的功能组件,所有代码都保存在components/RightClickMenu 下的文件夹中。功能组件的script标签中只有setup属性,使用普通 JavaScript 实现,不使用TypeScript。
功能要有,如下属性:
以下是制作 Vue3 右键菜单组件的方案:
属性 (Props)
菜单项配置
items: Array (必填)
MenuItem={id:string|number,//唯一标识label:string,//显示文本icon?:string,//可选图标disabled?:boolean,//禁用状态divider?:boolean,//是否为分隔线children?:MenuItem[],//子菜单handler?:Function//自定义点击处理}
定位控制
position: { x: number, y: number } (必填,通过右键事件坐标初始化)
autoAdjust: boolean (自动调整位置防止超出屏幕边界)
样式控制
theme: \’light\’ | \’dark\’ (预设主题样式)
customClass: string (自定义 CSS 类)
zIndex: number (层级控制)
行为控制
closeOnClick: boolean (点击后自动关闭)
showIcons: boolean (全局图标显示开关)
事件 (Emits)
item-click (携带被点击的 MenuItem 对象)
contextmenu-open (菜单打开时触发)
contextmenu-close (菜单关闭时触发)
before-close (关闭前可拦截)
其他
键盘导航
方向键导航
Enter 确认选择
ESC 关闭菜单
边缘处理
自动翻转子菜单位置
屏幕边界碰撞检测
可访问性
正确的 ARIA 角色属性
键盘焦点管理
样式建议
使用 position: fixed 定位
内置过渡动画
提供 CSS 变量主题化
扩展功能
通过插槽支持自定义菜单项模板
支持多级嵌套子菜单
全局右键事件管理
最佳实践
在组件挂载时添加全局点击监听
阻止浏览器默认右键菜单
提供 TypeScript 类型定义
建议优先实现核心功能,再逐步扩展高级特性。可通过配置优先 + 插槽扩展的方式平衡灵活性和易用性。
你有更好的建议也可以添加,要注明。组件定义好后给出3个及以上的调用示例。
下面是现有目录
vueAndDeepseek/
├── src/ # 源代码目录
│ ├── assets/ # 静态资源
│ │ ├── base.css
│ │ ├── main.css
│ │ └── logo.svg
│ ├── components/ # 组件目录
│ │ ├── HelloWorld.vue
│ │ ├── TheWelcome.vue
│ │ ├── WelcomeItem.vue
│ │ ├── Progress/
│ │ │ └── Progress.vue
│ │ ├── Accordion/
│ │ ├── BackToTop/
│ │ ├── Card/
│ │ ├── InfiniteScroll/
│ │ ├── Notification/
│ │ ├── Timeline/
│ │ ├── Switch/
│ │ ├── Tabs/
│ │ ├── Sidebar/
│ │ ├── Breadcrumbs/
│ │ ├── MasonryLayout/
│ │ ├── Rating/
│ │ ├── ColorPicker/
│ │ ├── RightClickMenu/
│ │ ├── DatePicker/
│ │ └── icons/
│ ├── router/ # 路由配置
│ │ └── index.js
│ ├── stores/ # Pinia 状态管理
│ │ └── counter.js
│ ├── views/ # 页面组件
│ │ ├── ProgressView.vue
│ │ ├── TabsView.vue
│ │ ├── SwitchView.vue
│ │ ├── TimelineView.vue
│ │ ├── NotificationView.vue
│ │ ├── CardView.vue
│ │ ├── InfiniteScrollView.vue
│ │ ├── BackToTopView.vue
│ │ ├── AccordionView.vue
│ │ ├── SidebarView.vue
│ │ ├── BreadcrumbsView.vue
│ │ ├── MasonryLayoutView.vue
│ │ ├── RatingView.vue
│ │ ├── ColorPickerView.vue
│ │ ├── RightClickMenuView.vue
│ │ ├── DatePickerView.vue
│ │ └── AboutView.vue
│ ├── App.vue # 根组件
│ └── main.js # 应用入口
├── public/ # 公共资源目录
├── index.html # HTML 模板
├── package.json # 项目配置
├── vite.config.js # Vite 配置
└── node_modules/ # 依赖包目录
📘组件代码 src\\components\\RightClickMenu\\RightClickMenu.vue
import{ref,onMounted,onUnmounted}from\'vue\';constprops=defineProps({items:{type:Array,required:true,default:()=>[]},theme:{type:String,default:\'light\'},customClass:{type:String,default:\'\'},zIndex:{type:Number,default:1000},closeOnClick:{type:Boolean,default:true},showIcons:{type:Boolean,default:true}});constemit=defineEmits([\'item-click\',\'contextmenu-open\']);constvisible=ref(false);constposition=ref({x:0,y:0});constmenuRef=ref(null);//显示菜单constshow=(event)=>{event.preventDefault();position.value={x:event.clientX,y:event.clientY};visible.value=true;emit(\'contextmenu-open\');//添加全局点击事件监听document.addEventListener(\'click\',handleClickOutside);};//隐藏菜单consthide=()=>{visible.value=false;document.removeEventListener(\'click\',handleClickOutside);};//处理菜单项点击consthandleItemClick=(item)=>{if(item.disabled)return;emit(\'item-click\',item);if(item.handler){item.handler(item);}if(props.closeOnClick){hide();}};//处理点击外部consthandleClickOutside=(event)=>{if(menuRef.value&&!menuRef.value.contains(event.target)){hide();}};//组件卸载时清理onUnmounted(()=>{document.removeEventListener(\'click\',handleClickOutside);});//暴露方法给父组件defineExpose({show,hide});{{item.label}}▶
📘定义组件 src\\views\\RightClickMenuView.vue
import{ref}from\'vue\';importRightClickMenufrom\'../components/RightClickMenu/RightClickMenu.vue\';constmenuRef1=ref(null);constmenuRef2=ref(null);constmenuRef3=ref(null);//基础菜单项配置constbasicMenuItems=[{id:\'copy\',label:\'复制\',icon:\'icon-copy\',handler:()=>console.log(\'复制\')},{id:\'paste\',label:\'粘贴\',icon:\'icon-paste\'},{divider:true},{id:\'delete\',label:\'删除\',icon:\'icon-delete\'}];//带子菜单的配置constnestedMenuItems=[{id:\'file\',label:\'文件\',children:[{id:\'new\',label:\'新建\',icon:\'icon-new\'},{id:\'open\',label:\'打开\',icon:\'icon-open\'}]},{id:\'edit\',label:\'编辑\',children:[{id:\'copy\',label:\'复制\',icon:\'icon-copy\'},{id:\'paste\',label:\'粘贴\',icon:\'icon-paste\',disabled:true}]}];//自定义菜单项constcustomMenuItems=[{id:\'share\',label:\'分享到\',children:[{id:\'wechat\',label:\'微信\',icon:\'icon-wechat\'},{id:\'weibo\',label:\'微博\',icon:\'icon-weibo\'}]},{divider:true},{id:\'delete\',label:\'删除\',icon:\'icon-delete\',disabled:true}];//事件处理函数consthandleContextMenu1=(event)=>{event.preventDefault();menuRef1.value?.show(event);};consthandleContextMenu2=(event)=>{event.preventDefault();menuRef2.value?.show(event);};consthandleContextMenu3=(event)=>{event.preventDefault();menuRef3.value?.show(event);};consthandleItemClick=(item)=>{console.log(\'菜单项被点击:\',item);};consthandleContextMenuOpen=()=>{console.log(\'右键菜单已打开\');};右键菜单示例
基础用法
在此区域右键点击查看基础菜单
多级菜单+深色主题
在此区域右键点击查看多级菜单
自定义样式
在此区域右键点击查看自定义样式菜单