使用extend写一个弹框组件

在这里插入图片描述

效果

在这里插入图片描述

PopperInput.vue文件
<template>
  <div
    class="popper-input"
    ref="popperInputRef"
    :style="{ top: `${top}px`, left: `${left}px` }"
  >
    <div class="arrow"></div>
    <div class="create-action create-tag" :style="{ width: `${176}px` }">
      <el-input
        type="number"
        v-model.trim="input"
        size="small"
        maxlength="10"
        style="width: 100%"
      ></el-input>
      <div class="btn">
        <el-button type="text" size="mini" @click="handleConfirm"
          >确定</el-button
        >
        <el-button type="text" size="mini" @click="handleHide">取消</el-button>
      </div>
    </div>
  </div>
</template>

<script>
import PopperInput from "./index";
export default {
  name: "PopperInput",
  props: {
    dom: {},
    inputValue: {
      type: String,
      default: "",
    },
  },
  data() {
    return {
      input: "",
      top: 0,
      left: 0,
    };
  },
  watch: {
    inputValue: {
      immediate: true,
      handler: function (newV) {
        this.input = newV;
      },
    },
  },
  mounted() {
    this.handlePosition();
    window.addEventListener("scroll", this.handlePosition.bind(this), true);
  },
  methods: {
    handleHide() {
      PopperInput.hide();
    },
    // 设置弹框位置
    handlePosition() {
      const rect = this.dom.getBoundingClientRect();
      // 弹框的长度为200
      const popperWidth = 200;
      this.top = rect.top + rect.height + 10;
      this.left = rect.left - popperWidth / 2 + rect.width / 2;
    },
    handleConfirm() {
      PopperInput.confirm(this.input);
    },
  },
};
</script>

<style lang="scss" scoped>
.popper-input {
  position: absolute;
  top: 50%;
  left: 50%;
  background: #fff;
  min-width: 150px;
  border-radius: 4px;
  border: 1px solid #ebeef5;
  padding: 12px;
  z-index: 2000;
  color: #606266;
  line-height: 1.4;
  text-align: justify;
  font-size: 14px;
  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
  word-break: break-all;
  .arrow {
    position: absolute;
    display: block;
    width: 0;
    height: 0;
    border-color: transparent;
    border-style: solid;
    border-width: 6px;
    border-top-width: 0;
    border-bottom-color: #b5b5b5;
    top: -6px;
    left: 50%;
    transform: translateX(-50%);
    &::after {
      display: block;
      border-color: transparent;
      border-style: solid;
      content: " ";
      border-width: 6px;
      border-top-width: 0;
      border-bottom-color: #fff;
      transform: translateX(-50%);
    }
  }
}
.create-action {
  display: flex;
  align-items: center;
  justify-content: center;
}
.create-tag {
  width: 230px;
  justify-content: flex-start;
  .el-input {
    width: 165px;
  }
  .btn {
    display: flex;
    flex-direction: row;
    .el-button {
      margin-left: 3px;
    }
  }
}
</style>
index.js
import Vue from 'vue';
import _PopperInput from "./PopperInput"

let defaultCallBack, popperInput
Vue.component(_PopperInput.name, _PopperInput)

const PopperInput = {
    show: function (props, callback) {
        if (!popperInput) {
            const PopperInput = Vue.extend({
                render(h) {
                    return h('popper-input', { props })
                }
            })
            popperInput = new PopperInput()
            this.vm = popperInput.$mount()
            document.body.appendChild(this.vm.$el)
            callback && (defaultCallBack = callback)
        }
    },
    confirm: function (val) {
        console.log(popperInput);
        defaultCallBack(val)
    },
    hide: function () {
        document.body.removeChild(this.vm.$el)
        popperInput.$destroy()
        popperInput = null
        this.vm = null
    }

}

export default PopperInput;
export { PopperInput };
在vue文件中使用
<template>
	<button @click="handlePop($event)">弹窗</button>
</template>
<script>
// 上面两个文件存放的位置
import PopperInput from "@/components/public/PopperInput";
export defalut{
	methods:{
		handlePop(event){
			PopperInput.show({
	          dom: event.target,
	          inputValue: "",
	        },
	        (value) => {
	        	// 输入框的值
	          	console.log(value);
	        }
	      );
		}
	}
}
</script>
标题“51单片机通过MPU6050-DMP获取姿态角例程”解析 “51单片机通过MPU6050-DMP获取姿态角例程”是一个基于51系列单片机(一种常见的8位微控制器)的程序示例,用于读取MPU6050传感器的数据,并通过其内置的数字运动处理器(DMP)计算设备的姿态角(如倾斜角度、旋转角度等)。MPU6050是一款集成三轴加速度计和三轴陀螺仪的六自由度传感器,广泛应用于运动控制和姿态检测领域。该例程利用MPU6050的DMP功能,由DMP处理复杂的运动学算法,例如姿态融合,将加速度计和陀螺仪的数据进行整合,从而提供稳定且实时的姿态估计,减轻主控MCU的计算负担。最终,姿态角数据通过LCD1602显示屏以字符形式可视化展示,为用户提供直观的反馈。 从标签“51单片机 6050”可知,该项目主要涉及51单片机和MPU6050传感器这两个关键硬件组件。51单片机基于8051内核,因编程简单、成本低而被广泛应用;MPU6050作为惯性测量单元(IMU),可测量设备的线性和角速度。文件名“51-DMP-NET”可能表示这是一个与51单片机及DMP相关的网络资源或代码库,其中可能包含C语言等适合51单片机的编程语言的源代码、配置文件、用户手册、示例程序,以及可能的调试工具或IDE项目文件。 实现该项目需以下步骤:首先是硬件连接,将51单片机与MPU6050通过I2C接口正确连接,同时将LCD1602连接到51单片机的串行数据线和控制线上;接着是初始化设置,配置51单片机的I/O端口,初始化I2C通信协议,设置MPU6050的工作模式和数据输出速率;然后是DMP配置,启用MPU6050的DMP功能,加载预编译的DMP固件,并设置DMP输出数据的中断;之后是数据读取,通过中断服务程序从DMP接收姿态角数据,数据通常以四元数或欧拉角形式呈现;再接着是数据显示,将姿态角数据转换为可读的度数格
<think>好的,我现在需要在Vue项目中手动创建一个,而不使用第三方组件库。我之前了解过一些Vue的基础知识,比如组件的创建和通信,但具体怎么实现还需要仔细想想。 首先,通常是一个独立的组件,所以应该先创建一个组件。这个组件需要能够显示和隐藏,可能需要用v-if或者v-show来控制。然后,组件的内容可能需要通过插槽或者props来自定义,比如标题和内容。 接下来,组件的显示和隐藏状态应该由父组件控制。父组件可能需要一个按钮来触发显示,这时候可以通过子组件的引用或者事件来通信。记得在子组件中,当用户点击关闭按钮时,应该触发一个事件通知父组件隐藏,比如使用$emit。 另外,遮罩层也是一个常见的需求,点击遮罩层关闭。这时候需要在组件的模板里添加一个遮罩层的div,并绑定点击事件。但要注意,点击内容本身不应该关闭,所以需要阻止事件冒泡或者判断点击的目标是不是遮罩层本身。 样式方面,可能需要固定定位,让居中显示,遮罩层覆盖整个屏幕。这需要一些CSS技巧,比如position: fixed,top和left设为0,width和height100%,背景半透明。内容则居中,可能需要使用transform或者flex布局。 动态创建组件的话,可能需要用到Vue.extend和手动挂载到DOM上,但这样会比较复杂,尤其是处理多个实例的时候。不过如果只是简单的需求,可能直接在父组件里引入组件,用v-if控制显示更简单。 还有,父子组件之间的通信方式,比如props传递是否显示的状态,子组件通过$emit来通知父组件更新这个状态。或者使用refs来直接调用子组件的方法,比如show和hide。 可能遇到的问题包括:如何确保在多次打开关闭后状态正确,如何处理多个同时存在的情况,以及如何让的内容灵活可变。这时候可能需要插槽来插入任意内容,或者通过props传递具体的文本和按钮配置。 最后,测试一下的各种交互,比如点击关闭按钮、点击遮罩层关闭、键盘ESC键关闭等,确保用户体验良好。同时,考虑动画效果,让的出现和消失更平滑,可能需要Vue的过渡组件。 总结步骤应该是:创建组件,处理显示隐藏状态,父子组件通信,添加样式和交互,测试并优化。</think>在Vue项目中通过原生方式实现出窗口功能,可通过以下步骤完成: --- ### 一、创建组件 新建`Modal.vue`文件,定义模板和逻辑: ```vue <template> <div v-if="visible"> <!-- 遮罩层 --> <div class="modal-mask" @click.self="handleClose"></div> <!-- 主体 --> <div class="modal-container"> <div class="modal-header"> <h3>{{ title }}</h3> <button @click="handleClose">×</button> </div> <div class="modal-body"> <slot></slot> <!-- 内容插槽 --> </div> <div class="modal-footer"> <button @click="handleConfirm">确认</button> <button @click="handleClose">取消</button> </div> </div> </div> </template> <script> export default { props: { visible: { type: Boolean, default: false }, title: { type: String, default: '提示' } }, methods: { handleClose() { this.$emit('update:visible', false) // 双向绑定更新[^3] }, handleConfirm() { this.$emit('confirm') this.handleClose() } } } </script> <style scoped> .modal-mask { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); } .modal-container { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: white; padding: 20px; } </style> ``` --- ### 二、父组件调用逻辑 在父组件中通过`v-model`控制显隐: ```vue <template> <div> <button @click="showModal = true">打开</button> <!-- 组件 --> <Modal v-model:visible="showModal" title="自定义标题" @confirm="handleConfirm" > <p>这是自定义内容区域</p> </Modal> </div> </template> <script> import Modal from './Modal.vue' export default { components: { Modal }, data() { return { showModal: false } }, methods: { handleConfirm() { console.log('用户点击了确认按钮') } } } </script> ``` --- ### 三、关键技术点解析 1. **组件通信** - 使用`v-model`实现双向绑定,等价于`:visible="showModal" @update:visible="val => showModal = val"` - 通过`$emit`触发父组件事件 2. **插槽机制** 使用`<slot>`标签实现内容自定义,允许插入任意模板代码 3. **遮罩层交互** `@click.self`修饰符确保只有点击遮罩层本身才触发关闭 4. **定位方案** `transform: translate(-50%, -50%)`实现垂直水平居中 --- ### 四、扩展优化建议 1. **动画效果** 添加`<transition>`包裹容器,实现淡入淡出动画 ```vue <transition name="fade"> <div v-if="visible" class="modal-mask"> <!-- 内容 --> </div> </transition> ``` 2. **键盘事件** 监听ESC键关闭: ```js mounted() { document.addEventListener('keyup', e => { if (e.key === 'Escape') this.handleClose() }) } ``` 3. **多管理** 可通过Vuex维护全局状态堆栈,实现多层窗控制 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值