Vue封装一个Dialog(对话框)组件

前几天被人问到过,当时挺懵的,回来看了看,发现原来是这样做的

效果图如下,只实现了一个最基本的,可以传入属性,也可以使用方法,后续需要什么,再往里面加

  主要是通过v-model控制对话框的显示和隐藏,实现父子组件的双向数据绑定

  • v-model用在组件上,默认绑定的值为modelValue,相当于v-model:modelValue。当然也可以自定义值
  • 父组件中子组件标签里的内容,子组件用插槽进行接收

父组件

<template>
  <div class="dialog">
    <button @click.stop="showModel">显示对话框 -- {
   
   { show }}</button>
    <Diglog
      v-model="show"
      :title="title"
      @open="handleOpen"
      @close="handleClose"
    >
      <p>插槽内容</p>
      <template #footer>
        <span>
          <button @click="show = false">确定</button>
          <button @click="show = false">取消</button>
        </span>
      </template>
    </Diglog>
  </div>
</template>

<script setup>
import { ref } from 'vue'
import Diglog from './components/Diglog.vue'
// 控制对话框显示/隐藏
let show = ref(false)
// 窗口标题
let title = ref('标题呀')

const showModel = () => {
  show.value = true
}

const handleOpen = (a) => {
  console.log('打开了' + '默认参数为' + a)
}

const handleClose = (a) => {
  console.log('关闭了' + '默认参数为' + a)
}
</script>

子组件

<template>
  <div class="box" ref="box" v-show="props.modelValue">
    <i class="close" @click="closeModel">X</i>
    <h3 class="title">{
   
   { props.title }}</h3>
    <slot>插槽备用内容1</slot>
    <footer class="footer">
      <slot name="footer">插槽备用内容2</slot>
    </footer>
  </div>
</template>

<script setup>
import { defineProps, defineEmits, ref, watch } from 'vue'

const props = defineProps({
  modelValue: {
    type: Boolean,
    default: false,
  },
  title: {
    type: String,
    default: '',
  },
})

let box = ref(null)

const emits = defineEmits(['update:modelValue', 'open', 'close'])

document.addEventListener('click', function (e) {
  if (!box.value.contains(e.target)) {
    emits('update:modelValue', false)
  }
})

const closeModel = () => {
  emits('update:modelValue', false)
}

watch(
  // 这种写法会侦听到 props 中 test 的变化
  () => props.modelValue,
  (newValue) => {
    // 打开了
    if (newValue === true) {
      emits('open', '随便传个参数吧1')
    }
    // 关闭了
    if (newValue === false) {
      emits('close', '随便传个参数吧2')
    }
  }
)
</script>

<style scoped>
.box {
  position: relative;
  width: 500px;
  height: 300px;
  background-color: pink;
  margin: auto;
}

.title {
  text-align: center;
  height: 30px;
  background: skyblue;
}

.close {
  display: block;
  position: absolute;
  top: 0;
  right: 0;
  width: 30px;
  text-align: center;
  line-height: 30px;
  background-color: #ccc;
  cursor: pointer;
}

.footer {
  position: absolute;
  bottom: 0;
  right: 0;
}
</style>

猜你喜欢

转载自blog.csdn.net/qq_52845451/article/details/130233557