VUE实现多个弹窗优先级变化实现思路

在开发复杂的单页应用(SPA)时,我们经常会遇到需要管理多个浮动窗口(或称“弹窗”、“面板”)的场景。一个核心的用户体验要求是:用户当前操作的窗口应该总是在最顶层。本文将结合代码示例,总结一种在 Vue 3 (Composition API) 和 TypeScript 环境下,实现这一功能的清晰、可扩展的思路。

核心思路

该功能的实现主要依赖于三个关键部分:

  • 集中式状态管理:使用一个响应式对象统一管理所有窗口的 z-index 层级。
  • 点击置顶:当用户点击某个窗口时,动态提升其 z-index 到最高。
  • 新窗口置顶:当一个新窗口被打开时,自动将其 z-index 设置为最高

实现步骤详解

1. 状态设计:统一管理 z-index

首先,我们需要一个地方来存储和跟踪所有浮动窗口的层级状态。在 Vue 3 的 setup 函数中,使用 reactive API 是一个绝佳的选择,因为它创建了一个响应式对象,任何对此对象的修改都会自动触发 UI 更新。

import { reactive } from 'vue';// --- 浮动窗口层级管理 ---
const windowZIndices = reactive<Record<string, number>>({cockpitBox: 10,realTimeWarning: 10,monitorBox: 10,historyEvent: 10,sampleNorth: 10,blackAndWhiteList: 10,shipList: 10,// ... 其他窗口
});
  • reactive:确保了当 z-index 值变化时,视图能够自动重新渲染。
  • Record<string, number>:这是一个 TypeScript 类型,定义了一个键是字符串(窗口名)、值是数字(z-index)的对象。
  • 初始值:所有窗口的初始 z-index 都设为 10,表示它们在初始状态下层级相同

接着,在模板中,我们将每个窗口组件的 style 属性与这个 reactive 对象中的相应值进行绑定

<!-- 模板部分 -->
<div class="cockpitBox" @mousedown="(e) => bringToFront(e, 'cockpitBox')":style="{ zIndex: windowZIndices['cockpitBox'] }"><!-- ... -->
</div><RealTimeWarning v-if="store.showRealTimeWarning"@mousedown="(e) => bringToFront(e, 'realTimeWarning')":style="{ zIndex: windowZIndices['realTimeWarning'] }" /><ShipList class="shipList" v-if="store.showShipList"@mousedown="(e) => bringToFront(e, 'shipList')":style="{ zIndex: windowZIndices['shipList'] }" /><!-- ... 其他窗口组件 -->

2. 核心逻辑:bringWindowToFront 函数

这是实现“点击置顶”功能的核心。当一个窗口需要被置顶时,我们需要找到当前所有窗口中的最大 z-index,然后将目标窗口的 z-index 设置为这个最大值加一。

/*** 将指定名称的窗口置于顶层(z-index 最高)。* @param windowName 要置顶的窗口名称*/
const bringWindowToFront = (windowName: keyof typeof windowZIndices) => {// 1. 获取当前所有 z-index 值的最大值const maxZIndex = Math.max(...Object.values(windowZIndices));// 2. 将目标窗口的 z-index 设为最大值 + 1windowZIndices[windowName] = maxZIndex + 1;
};

为了在用户点击时调用它,我们为每个窗口绑定了 @mousedown 事件,该事件会调用一个简单的包装函数 bringToFront

const bringToFront = (event: MouseEvent, windowName: keyof typeof windowZIndices) => {bringWindowToFront(windowName);
};

3. 自动管理:新开窗口置顶

除了点击置顶,新打开的窗口也应该自动显示在最前面。这个功能通过 watch API 来实现,我们侦听控制每个窗口可见性的状态(通常是 Pinia store 中的一个布尔值)。

import { watch } from 'vue';const setupWindowManagement = () => {// 定义需要管理的窗口及其对应的 store 状态const windowsToManage: Record<string, () => boolean> = {realTimeWarning: () => store.showRealTimeWarning,monitorBox: () => store.showMonitor,historyEvent: () => store.showHistory,// ... 其他由 store 控制显隐的窗口};// 遍历并为每个窗口设置 watch 侦听器
//Object.prototype.hasOwnProperty.call用于判断一个属性是否是对象自身的属性for (const windowName in windowsToManage) {if (Object.prototype.hasOwnProperty.call(windowsToManage, windowName)) {const typedWindowName = windowName as keyof typeof windowZIndices;watch(windowsToManage[typedWindowName], (newValue, oldValue) => {// 当窗口从“不显示”变为“显示”时if (newValue && !oldValue) {// 调用置顶函数bringWindowToFront(typedWindowName);}});}}
};

最后,在 onMounted生命周期钩子中调用 setupWindowManagement(),即可在组件挂载后激活这些侦听器。

总结

通过结合 reactive 状态、事件处理和 watch 侦听器,我们构建了一个清晰、高效且易于维护的浮动窗口层级管理系统:

  • reactive 对象:作为单一数据源,集中管理所有窗口的 z-index。
  • @mousedown 事件:响应用户的直接交互,提供即时的“点击置顶”反馈。
  • watch 侦听器:自动化处理程序状态变化(如窗口的显示/隐藏),确保新窗口始终拥有最高优先级。

这种方法不仅代码结构清晰,而且扩展性强。当需要添加新的浮动窗口时,只需在 windowZIndices 对象和 windowsToManage 映射中增加相应的条目即可,无需改动核心逻辑。




tips

1.for...in 循环

  • 定义:for...in 是 JavaScript 中用于遍历对象属性的一种循环。它会遍历一个对象上所有可枚举的属性(包括自有属性和从原型链上继承的属性)。
  • 作用:在这个场景下,它会依次遍历 windowsToManage 对象的每一个键(key)。
  • 第一次循环,windowName 的值是字符串 'realTimeWarning'。
  • 第二次循环,windowName 的值是字符串 'monitorBox'。
  • ...以此类推,直到所有窗口都遍历完。
  • 目的:通过这个循环,我们可以为每一个在 windowsToManage 中配置的窗口都应用上相同的逻辑(也就是给它们都设置一个 watch 侦听器)。

2. Object.prototype.hasOwnProperty.call()

hasOwnProperty 是什么?

  • 每个 JavaScript 对象都有一个 hasOwnProperty('propertyName') 方法,它用来判断一个属性是对象自身的属性,还是从原型链上继承来的。如果是自身的,返回 true;如果是继承的,返回 false。
  • 为什么需要它?
  • for...in 循环有一个特点,它不仅会遍历对象自身的属性,还会遍历其原型链上的属性。在绝大多数情况下,我们只关心对象自身的属性。这个 if 判断就是为了过滤掉那些可能存在的、我们不关心的继承属性。
  • 为什么不直接写 windowsToManage.hasOwnProperty(windowName)?
  • 直接写 windowsToManage.hasOwnProperty(...) 在 99% 的情况下是没问题的。但 Object.prototype.hasOwnProperty.call(...) 是一个更安全、更健壮的写法,主要为了防止两种极端情况:
  1. 对象重写了 hasOwnProperty:如果 windowsToManage 对象恰好有一个自己的属性也叫 hasOwnProperty,那么直接调用就会执行被重写的版本,可能导致非预期的结果。
  1. 对象没有 hasOwnProperty 方法:如果一个对象是通过 Object.create(null) 创建的,那么它没有任何原型,也就不存在 hasOwnProperty 方法,直接调用会报错。
  • .call() 的作用:
  • call 允许我们调用一个函数,并且手动指定这个函数内部的 this 指向。
  • Object.prototype.hasOwnProperty.call(windowsToManage, windowName) 的意思是:
  1. 找到 Object 原型上最原始、最正宗的那个 hasOwnProperty 方法。
  1. 通过 .call() 来执行它。
  1. 第一个参数 windowsToManage 是告诉 hasOwnProperty:“请把 this 当作是 windowsToManage 对象来执行”。
  1. 第二个参数 windowName 是传递给 hasOwnProperty 的参数。
  • 这样就保证了无论 windowsToManage 对象本身是什么样,我们调用的始终是正确、安全的 hasOwnProperty 方法。这在编写高质量的库或框架代码时是一个非常重要的最佳实践。

typeof  和 keyof typeof

typeof (在类型上下文中使用)

      功能:获取一个变量或对象的类型。它允许我们基于已存在的 JavaScript 代码(值)来      创建 TypeScript 类型。(从变量提取类型,函数返回值类型推断)

        const person = { name: "Alice", age: 30 };// typeof person 的结果是类型:type PersonType = typeof person; //相对于type PersonType = { name: string, age: number }

keyof(直接操作类型)

   功能:获取一个类型的所有键(key),并创建一个由这些键组成的联合类型 (Union Type)

(我有个类型,想知道它有哪些属性)

        type PersonType = { name: string, age: number };// keyof PersonType 的结果是类型:type PersonKeys = keyof PersonType; //相对于 type PersonKeys="name" | "age"

keyof typeof ( 通过值获取类型再获取键名)

则会进一步获取这个类型的所有键,形成一个联合类型(我有个变量,想知道它有哪些属性)

// 先有变量(值)
const person = {name: "Alice",age: 30,email: "alice@example.com"
};// 通过变量获取类型,再获取键名
type PersonKeys = keyof typeof person;
// 结果: "name" | "age" | "email"

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/94065.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/94065.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

集成算法和kmeans

一、集成算法&#xff08;Ensemble Learning&#xff09; 1. 基本概念 集成学习通过构建并结合多个学习器&#xff08;基分类器/回归器&#xff09;来完成学习任务&#xff0c;旨在通过集体决策提升模型性能&#xff0c;类似于“多个专家的综合判断优于单个专家”。 2. 结合策略…

图数据库性能与可扩展性评估

图数据库的性能与可扩展性直接决定业务场景&#xff08;如实时风控、知识图谱分析&#xff09;的落地效果&#xff0c;需结合业务场景特性&#xff08;OLTP/OLAP&#xff09;、技术指标&#xff08;响应时间、吞吐量&#xff09;和扩展能力&#xff08;数据量/节点扩展&#xf…

树莓派常用的国内镜像源列表以及配置方法

1. 常用的镜像源使用下来发现清华源经常访问不到&#xff0c;阿里源比较好用。其他源还未测试。源名称URL清华源https://pypi.tuna.tsinghua.edu.cn/simple阿里云https://mirrors.aliyun.com/pypi/simple/中科大https://pypi.mirrors.ustc.edu.cn/simple/华为云https://repo.hu…

Transformer在文本、图像和点云数据中的应用——经典工作梳理

摘要 最近在整一些3D检测和分割的任务&#xff0c;接触了一下ptv3&#xff0c;在之前梳理的工作owlv2中用到了vit&#xff0c;去年年假阅读《多模态大模型&#xff1a;算法、应用与微调》&#xff08;刘兆峰&#xff09;时学习了Transformer网络架构及其在文本数据中的应用&am…

训练后数据集后部署PaddleOCR转trt流程

训练后的模型部署&#xff0c;首先要进行训练 0.训练流程见文章 PaddleOCR字符识别&#xff0c;训练自己的数据集全流程&#xff08;环境、标注、训练、推理&#xff09;-CSDN博客文章浏览阅读1.6k次&#xff0c;点赞53次&#xff0c;收藏23次。PaddleOCR是基于百度飞桨框架的…

《MLB美职棒》美国国球是橄榄球还是棒球·棒球5号位

USAs National Sport Showdown: MLB⚾️ vs NFL Ultimate Guide!从商业价值到文化基因&#xff0c;360解析美国体育王座之争&#xff01;添加图片注释&#xff0c;不超过 140 字&#xff08;可选&#xff09;️ 历史定位 Historical Roots⚾️ MLB&#xff1a;The "Classi…

常见 Linux 网络命令梳理

在日常运维和排障工作中&#xff0c;网络相关命令是最常用的一类工具。无论是检查网络连通性&#xff0c;还是定位路由问题&#xff0c;又或是分析端口和服务占用&#xff0c;熟悉这些命令都能让我们更高效地解决问题。本文将从几个常见的维度来梳理 Linux 下的网络命令&#x…

Docker 搭建 Gitlab 实现自动部署Vue项目

1、配置要求: 硬件要求: CPU:双核或以上 内存:4GB或以上 软件要求:Centos6 或更高版本 2、gitlab镜像: # 中文版仓库 #docker pull twang2218/gitlab-ce-zh docker pull gitlab/gitlab-ce 3、gitlab部署目录 说明:为了跟其他容器区分,gitlab相关容…

如何解决机器翻译的“幻觉“问题(Hallucination)?

更多内容请见: 机器翻译修炼-专栏介绍和目录 文章目录 一、数据层面优化 二、模型架构改进 三、训练策略调整 四、评估与迭代 五、前沿方向与挑战 六、案例:WMT2023幻觉缓解方案 机器翻译中的“幻觉”(Hallucination)指模型生成与源文本语义无关、逻辑矛盾或事实错误的翻译…

基于STM32+NBIOT设计的宿舍安防控制系统_264

文章目录 1.1 项目介绍 【1】开发背景 【2】实现需求 【3】项目硬件模块组成 【4】设计意义 【5】国内外研究现状 【6】摘要 1.2 系统总体设计 【1】系统功能需求分析 【2】系统总体方案设计 【3】系统工作原理 1.3 系统框架图 1.4 系统功能总结 1.5 系统原理图 1.6 实物图 1.7…

SLAM文献之-Globally Consistent and Tightly Coupled 3D LiDAR Inertial Mapping

一、简介 该论《Globally Consistent and Tightly Coupled 3D LiDAR Inertial Mapping》是日本先进工业科学技术研究所&#xff08;AIST&#xff09;的Koide等人于2022年在IEEE国际机器人与自动化会议&#xff08;ICRA&#xff09;上发表的一篇论文。该研究提出了一种基于全局…

【STM32】HAL库中的实现(七):DMA(直接存储器访问)

DMA 是什么&#xff1f; DMA&#xff08;Direct Memory Access&#xff09;是 外设直接和内存之间数据搬运的机制&#xff0c;不需要 CPU 参与。 ✅ 举个例子&#xff1a;传统方式&#xff1a; ADC → CPU → RAM 使用 DMA&#xff1a;ADC → DMA → RAM&#xff08;CPU 不需干…

【LeetCode热题100道笔记+动画】字母异位词分组

题目描述 给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。 示例 1: 输入: strs = [“eat”, “tea”, “tan”, “ate”, “nat”, “bat”] 输出: [[“bat”],[“nat”,“tan”],[“ate”,“eat”,“tea”]] 解释: 在 strs 中没有字符串可…

【Kafka】常见简单八股总结

为什么使用消息队列&#xff1f; 解耦&#xff1a; 我以我的一段开发经验举例&#xff1a; 【Kafka】登录日志处理的三次阶梯式优化实践&#xff1a;从同步写入到Kafka多分区批处理 我做过一个登录日志逻辑&#xff0c;就是在登录逻辑末尾&#xff0c;加一段写进数据库登录日志…

微信小程序连接到阿里云物联网平台

目录准备阶段阿里云配置下载mqtt.min.js文件小程序实现注意小程序配置服务器域名概述&#xff1a;介绍使用微信小程序连接到阿里云平台的快捷方法和完整过程。 阿里云平台建立设备&#xff0c;提供mqtt连接参数&#xff0c;小程序借助mqtt.min.js&#xff0c;也就是基于Github下…

2-3〔O҉S҉C҉P҉ ◈ 研记〕❘ 漏洞扫描▸AppScan(WEB扫描)

郑重声明&#xff1a; 本文所有安全知识与技术&#xff0c;仅用于探讨、研究及学习&#xff0c;严禁用于违反国家法律法规的非法活动。对于因不当使用相关内容造成的任何损失或法律责任&#xff0c;本人不承担任何责任。 如需转载&#xff0c;请注明出处且不得用于商业盈利。 …

LeetCode 刷题【47. 全排列 II】

47. 全排列 II 自己做 解1&#xff1a;检查重复 class Solution { public:void circle(vector<int> nums, vector<vector<int>> &res,int start){int len nums.size();if(start len - 1){ //到头了//检查重复bool is_exist fa…

Https之(一)TLS介绍及握手过程详解

文章目录简介 TLSTLS第一次握手1.Client HelloTLS第二次握手2.Server Hello3.Certificate4.Server Hello DoneTLS第三次握手5.Client Key Exchange6.Change Cipher Spec7.Encrypted Handshake MessageTLS第四次握手8.New Session Ticket9.Change Cipher Spec10.Encrypted Hands…

【WEB 】从零实现一个交互轮播图(附源码)

文章目录 一、轮播图整体功能规划二、HTML结构深度解析三、CSS样式实现细节1. 定位系统详解2. 显示/隐藏机制3. 按钮交互效果实现4. 纯CSS箭头实现5. 指示器&#xff1a;当前位置可视化 四、JavaScript逻辑深入解析1. 核心变量与DOM获取2. 图片切换函数&#xff08;核心逻辑&am…

机器学习--PCA降维

一核心部分 1解决的问题&#xff1a;应对高维数据带来的计算量大、冗余信息多、易出现过拟合等问题&#xff0c;在减少数据维度的同时尽可能保留原始数据的关键信息。2核心思想&#xff1a…