Vue弹窗封装

本文详细介绍了在Vue2.0和Vue3.0中封装弹窗组件的方法,包括如何使用v-model和.sync来同步父组件的可见性状态,以及如何通过自定义事件和方法实现弹窗的动态交互。

一、vue2.0

<template>
  <div class="na-dialog">
    <el-dialog :title="datas.title" v-model="innerVisible" :width="datas.width" @close="btnClick(datas.close)"
      @opened="btnClick(datas.open)">
      <slot></slot>
      <template #footer>
        <span class="dialog-footer">
          <el-button @click="innerVisible = false" size="small">取消</el-button>
          <template v-for="item in datas.btns">
            <el-button type="primary" @click="btnClick(item.methods)" size="small">{{ item.label }}</el-button>
          </template>
        </span>
      </template>
    </el-dialog>
  </div>
</template>
<script>
  export default {
    props: {
      visible: { //绑定显隐的字段
        type: Boolean,
        default: false
      },
      datas: {
        type: Object,
        default: { //弹窗数据
          title: '弹窗名称', //弹窗名称
          width: '70%', //弹窗宽度
          close: 'closeDialog', //弹窗关闭后调用
          btns: [ //按钮-取消不需要
            {
              label: '保存',
              methods: 'save'
            }
          ]
        }
      }
    },
    computed: {
      innerVisible: {
        get() {
          return this.visible
        },
        set(val) {
          debugger
          this.$emit('update:visible', val)
        }
      },
    },
    methods: {
      btnClick(key) {
        if (key) {
          this.$nextTick(() => {
            this.$parent[key]();
          })
        }
      },
    },
  }
</script>

<style lang="scss" scoped>
  .na-dialog {
    :deep(.el-dialog) {
      .el-dialog__body {
        padding: 0 !important;
        height: calc(100% - 90px);
      }
    }
  }
</style>
<na-dialog :visible.sync="visible" :data="dialog"></na-dialog>

父组件需要加入closeDialog方法、save方法

二、vue3.0弹窗封装

 

<template>
  <div class="na-dialog">
    <el-dialog :title="datas.title" v-model="innerVisible" :width="datas.width"
      @close="btnClick(datas.close)" @opened="btnClick(datas.open)">
      <slot></slot>
      <template #footer>
        <span class="dialog-footer">
          <el-button @click="innerVisible= false" size="small">取消</el-button>
          <template v-for="item in datas.btns">
            <el-button type="primary" @click="btnClick(item.methods)" size="small">{{ item.label }}</el-button>
          </template>
        </span>
      </template>
    </el-dialog>
  </div>
</template>
<script>
  import {
    reactive,
    toRefs,
    computed,
    getCurrentInstance,
    nextTick,
  } from "vue";
  export default {
    props: {
      visible: {//绑定显隐的字段
        type: Boolean,
        default: false
      },
      datas: {
        type: Object,
        default: {//弹窗数据
          title: '弹窗名称',//弹窗名称
          width: '70%', //弹窗宽度
          close: 'closeDialog', //弹窗关闭后调用
          btns: [ //按钮-取消不需要
            {
              label: '保存',
              methods: 'save'
            }
          ]
        }
      }
    },
    setup(props, context) {
      const state = reactive({});
      const instance = getCurrentInstance();
      const {
        proxy
      } = instance;
      const {
        visible
      } = toRefs(props);
      let innerVisible = computed({
        get: () => visible.value,
        set: (val) => {
          context.emit("update:visible", val);
        },
      })
      //按钮按下
      const btnClick = (key) => {
        if (key) {
          nextTick(()=>{
            proxy.$parent[key]();
          })
        }
      }
      return {
        ...toRefs(state),
        innerVisible,
        btnClick
      };
    }
  }
</script>

<style lang="scss" scoped>
  .na-dialog {
    :deep(.el-dialog) {
      .el-dialog__body {
        padding: 0 !important;
        height: calc(100% - 90px);
      }
    }
  }
</style>
<na-dialog v-model:visible="dialog.visible" :data="dialog"></na-dialog>

引入没什么不同,只是参数有一点区别,一个是参数后加.sync一个是参数前加v-model:

<think>根据给定的参考资料,用户想要了解在UniApp中基于Vue3封装弹窗组件的方法。通过分析引用内容,我们可以总结出两种主要方法:自定义封装和使用双向绑定。 引用[1]展示了一个自定义封装弹窗组件(my-dialog.vue)的使用方式。在父组件(如index.vue)中引入该组件,并通过ref控制弹窗的显示/隐藏。同时,可以传递标题、内容、按钮文字等props,并监听确认和取消事件。 引用[2]则展示了使用Vue3的双向绑定(通过computed的get和set)实现父子组件之间的状态同步。子组件弹窗通过computed属性visible来同步父组件传递的open属性,并通过emit事件更新父组件的状态。 结合这两者,我们可以给出在UniApp中使用Vue3封装弹窗组件的步骤和示例代码。 步骤: 1. 创建弹窗组件(如my-dialog.vue),定义所需的props(如title, content等)和事件(如confirm, cancel)。 2.弹窗组件内部,使用一个变量(如isShow)控制弹窗的显示与隐藏。注意:如果希望实现父子双向绑定,可以采用引用[2]的方式,使用computed属性与emit结合。 3. 在父组件中引入弹窗组件,并通过ref调用弹窗显示/隐藏的方法,或者通过双向绑定的方式控制弹窗。 下面提供两种实现方式: 方式一:通过ref控制(类似引用[1]) 弹窗组件(my-dialog.vue): ```vue <template> <view v-if="isShow" class="dialog-mask"> <view class="dialog-content"> <text class="dialog-title">{{ title }}</text> <text class="dialog-message">{{ content }}</text> <view class="dialog-buttons"> <button @click="handleCancel">{{ cancelText }}</button> <button @click="handleConfirm">{{ confirmText }}</button> </view> </view> </view> </template> <script setup> import { ref, defineProps, defineEmits } from 'vue' const props = defineProps({ title: String, content: String, confirmText: { type: String, default: '确认' }, cancelText: { type: String, default: '取消' } }) const emit = defineEmits(['clickConfirm', 'clickCancel']) const isShow = ref(false) // 显示弹窗 const show = () => { isShow.value = true } // 隐藏弹窗 const hide = () => { isShow.value = false } const handleConfirm = () => { emit('clickConfirm') hide() } const handleCancel = () => { emit('clickCancel') hide() } // 暴露方法给父组件调用 defineExpose({ show, hide }) </script> <style scoped> .dialog-mask { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0,0,0,0.5); display: flex; justify-content: center; align-items: center; z-index: 1000; } .dialog-content { background-color: #fff; width: 80%; border-radius: 10rpx; padding: 20rpx; display: flex; flex-direction: column; align-items: center; } .dialog-title { font-size: 36rpx; font-weight: bold; margin-bottom: 20rpx; } .dialog-message { font-size: 28rpx; margin-bottom: 40rpx; } .dialog-buttons { display: flex; flex-direction: row; justify-content: space-around; width: 100%; } </style> ``` 父组件(index.vue)中使用: ```vue <template> <view> <button @click="showDialog">打开弹窗</button> <my-dialog ref="dialogRef" :title="title" :content="content" :confirmText="confirmText" :cancelText="cancelText" @clickConfirm="handleConfirm" @clickCancel="handleCancel" /> </view> </template> <script setup> import { ref } from 'vue' import MyDialog from './my-dialog.vue' const dialogRef = ref(null) const title = ref('标题') const content = ref('这是弹窗内容') const confirmText = ref('确定') const cancelText = ref('关闭') const showDialog = () => { dialogRef.value.show() } const handleConfirm = () => { console.log('点击了确认') } const handleCancel = () => { console.log('点击了取消') } </script> ``` 方式二:使用双向绑定(类似引用[2]) 弹窗组件(my-dialog.vue): ```vue <template> <view v-if="visible" class="dialog-mask"> ...(与方式一相同的弹窗内容) </view> </template> <script setup> import { computed, defineProps, defineEmits } from 'vue' const props = defineProps({ modelValue: Boolean, // 用于双向绑定的值 title: String, content: String, confirmText: { type: String, default: '确认' }, cancelText: { type: String, default: '取消' } }) const emit = defineEmits(['update:modelValue', 'confirm', 'cancel']) const visible = computed({ get() { return props.modelValue }, set(val) { emit('update:modelValue', val) } }) const handleConfirm = () => { emit('confirm') visible.value = false } const handleCancel = () => { emit('cancel') visible.value = false } </script> ``` 父组件(index.vue)中使用: ```vue <template> <view> <button @click="isDialog = true">打开弹窗</button> <my-dialog v-model="isDialog" :title="title" :content="content" :confirmText="confirmText" :cancelText="cancelText" @confirm="handleConfirm" @cancel="handleCancel" /> </view> </template> <script setup> import { ref } from 'vue' import MyDialog from './my-dialog.vue' const isDialog = ref(false) const title = ref('标题') const content = ref('这是弹窗内容') const confirmText = ref('确定') const cancelText = ref('关闭') const handleConfirm = () => { console.log('点击了确认') } const handleCancel = () => { console.log('点击了取消') } </script> ``` 注意事项: 1. 样式可以根据需求自定义,比如字体、颜色、背景等。 2. 弹窗的显示/隐藏控制,根据选择的方式不同(ref或双向绑定)有不同的实现。 3. 事件处理:在弹窗组件内部触发事件,并在父组件中处理。 两种方式各有优劣: 方式一(使用ref)更直接,但父组件需要获取ref并调用组件的方法。 方式二(双向绑定)更符合Vue的数据驱动理念,父组件通过一个变量控制,逻辑更清晰。 以上两种方法都可以在UniApp中实现弹窗封装,选择哪种取决于个人偏好和项目需求。 根据提供的引用[3]还提到了添加客服弹窗组件,其中包含样式设置,我们可以参考其样式结构,但这里主要关注逻辑实现。 因此,在UniApp中基于Vue3封装弹窗组件,主要涉及组件的创建、props传递、事件触发以及两种控制显示隐藏的方式。 </think>以下是UniApp中基于Vue 3封装弹窗组件的详细方法,结合引用内容整理的最佳实践: ### 一、基础封装方法(推荐) ```vue <!-- components/YcDialog.vue --> <template> <view v-if="isDialog" class="dialog-mask"> <view class="dialog-content"> <text class="title">{{ title }}</text> <text class="content">{{ content }}</text> <view class="button-group"> <button @click="handleCancel">{{ cancelText }}</button> <button @click="handleConfirm">{{ confirmText }}</button> </view> </view> </view> </template> <script setup> import { ref } from 'vue' const props = defineProps({ title: String, content: String, confirmText: { type: String, default: '确认' }, cancelText: { type: String, default: '取消' } }) const emit = defineEmits(['clickConfirm', 'clickCancel']) const isDialog = ref(false) const handleConfirm = () => { isDialog.value = false emit('clickConfirm') } const handleCancel = () => { isDialog.value = false emit('clickCancel') } // 暴露控制方法给父组件 defineExpose({ show: () => isDialog.value = true, hide: () => isDialog.value = false }) </script> <style scoped> .dialog-mask { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); display: flex; justify-content: center; align-items: center; z-index: 1000; } .dialog-content { width: 80%; background: #fff; border-radius: 16rpx; padding: 32rpx; } .button-group { flex-direction: row; justify-content: space-around; margin-top: 40rpx; } </style> ``` ### 二、父组件调用方式 ```vue <!-- pages/index/index.vue --> <template> <view> <button @click="showDialog">打开弹窗</button> <yc-dialog ref="dialogRef" title="操作确认" content="确定要执行此操作吗?" @clickConfirm="onConfirm" @clickCancel="onCancel" /> </view> </template> <script setup> import { ref } from 'vue' import YcDialog from '@/components/YcDialog.vue' const dialogRef = ref(null) const showDialog = () => { dialogRef.value?.show() } const onConfirm = () => { console.log('用户点击确认') // 执行确认操作 } const onCancel = () => { console.log('用户点击取消') // 执行取消操作 } </script> ``` ### 三、双向绑定实现(高级用法) ```vue <!-- components/BindDialog.vue --> <script setup> import { computed } from 'vue' const props = defineProps({ open: Boolean, title: String }) const emit = defineEmits(['update:open']) const visible = computed({ get: () => props.open, set: (val) => emit('update:open', val) }) const handleClose = () => { visible.value = false } </script> <template> <view v-if="visible" class="dialog-wrap"> <!-- 弹窗内容 --> <button @click="handleClose">关闭</button> </view> </template> ``` **父组件调用**: ```vue <template> <bind-dialog v-model:open="showDialog" title="双向绑定弹窗" /> </template> <script setup> import { ref } from 'vue' const showDialog = ref(false) </script> ``` ### 关键注意事项 1. **组件通信**: - 普通模式:使用`defineExpose`暴露操作方法(推荐) - 高级模式:使用`v-model`双向绑定(需配合computed的get/set) 2. **样式定制**: - 通过CSS变量实现主题定制:`--dialog-bg-color` - 使用uni-app的rpx单位确保多端适配 - 添加动画效果提升体验(使用uni.createAnimation) 3. **最佳实践**: ```css /* 添加过渡动画 */ .dialog-content { transition: all 0.3s ease; transform: scale(0.8); opacity: 0; } .dialog-mask[data-visible="true"] .dialog-content { transform: scale(1); opacity: 1; } ``` 4. **多端兼容**: - 使用`uni.getSystemInfoSync()`动态计算弹窗尺寸 - 通过`process.env.UNI_PLATFORM`区分平台样式 - 处理微信小程序`cover-view`兼容问题 [^1][^2][^3]
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值