vue3如何优雅的使用element-plus的dialog

11,613 阅读3分钟

如何优雅的基于 element-plus,封装一个梦中情 dialog

优点

摆脱繁琐的 visible 的命名,以及反复的重复 dom。

想法

将 dialog 封装成一个函数就能唤起的组件。如下:

addDialog({
  title: "测试", //弹窗名
  component: TestVue, //组件
  width: "400px", //弹窗大小
  props: {
    //传给组件的参数
    id: 0
  },
  callBack: (data: any) => {
    //当弹窗任务结束后,调用父页面的回掉函数。(比如我新增完成了需要刷新列表页面)
    console.log("回调函数", data)
  }
})

效果图

dialog.gif

基于 el-dialog 进行初步封装

// index.ts
import { reactive } from "vue"
type dialogOptions = {
  title: string
  component: any
  props?: Object
  width: string
  visible?: any
  callBack?: Function
}
export const dialogList: dialogOptions[] = reactive([])

export const addDialog = (options: dialogOptions) => {
  dialogList.push(Object.assign(options, { visible: true }))
}

export const closeDialog = (item: dialogOptions, i: number, args?: any, isNativeClose?: boolean) => {
  dialogList.splice(i, 1)
  if (!isNativeClose) item.callBack && item.callBack(...args)
}

<template>
  <Teleport to="body">
    <el-dialog
      v-for="(item, index) in dialogList"
      :key="index"
      :title="item.title"
      :width="item.width"
      v-model="item.visible"
      @close="() => closeDialog(item, index, '', true)"
    >
      <component :is="item.component" v-bind="item.props" @close="(...args:any) => closeDialog(item, index, args)" />
    </el-dialog>
  </Teleport>
</template>

<script setup lang="ts">
  import { dialogList, closeDialog } from "./index"
</script>
  • 首先定义了 dialogList,它包含了所有弹窗的信息。
  • component 使用 componet is 去动态加载子组件
  • addDialog 调用唤起弹窗的函数
  • closeDialog 关闭弹窗的函数

在app.vue中挂载

<script setup>
import Mydialog from "@/components/gDialog/index.vue"
</script>

<template>
 <router-view />
 <Mydialog></Mydialog>
</template>

<style scoped>

</style>

使用

创建一个弹窗组件

<!-- test.vue -->
<template>
  父弹窗
  <el-button type="primary" @click="openChildDialog">打开子dialog</el-button>
  <el-button type="primary" @click="closeDialog">关闭弹窗</el-button>
</template>

<script setup lang="ts">
  import { addDialog } from "@/components/gDialog/index"
  import childVue from "./child.vue"
  const props = defineProps(["id"])
  console.log(props.id, "props")
  const emit = defineEmits(["close"])
  const closeDialog = () => {
    emit("close", 1, 2, 34)
  }
  const openChildDialog = () => {
    addDialog({
      title: "我是子dialog",
      width: "500px",
      component: childVue
    })
  }
</script>

在列表页面唤醒弹窗

<!-- list.vue -->
<template>
  列表页
  <el-button type="primary" @click="openDialog">打开dialog</el-button>
</template>
<script setup lang="ts">
import { addDialog } from "@/components/gDialog/index"
import TestDialog from "./test.vue"
const openDialog = () => {
  addDialog({
    title: "我是dialog",
    width: "500px",
    props:{
      id:0
    }
    component: TestDialog,
    callBack: (data: any) => {
      //当弹窗任务结束后,调用父页面的回掉函数。(比如我新增完成了需要刷新列表页面)
      console.log("回调函数", data)
    }
  })
}

多层级弹窗嵌套

<!-- child.vue -->
<template>
  子弹窗
  <el-button type="primary" @click="closeDialog">关闭弹窗</el-button>
</template>

<script setup lang="ts">
  import { addDialog } from "@/components/gDialog/index"
  const emit = defineEmits(["close"])
  const closeDialog = () => {
    emit("close", 1, 2, 34)
  }
</script>

使用场景

表单的增加修改校验

表格

<template>
  <el-card>
    <el-button type="primary" @click="handle('')">添加</el-button>
  </el-card>
  <el-table :data="tableList.data">
    <el-table-column lable="名称" prop="name" />
    <el-table-column lable="年龄" prop="age" />
    <el-table-column lable="操作">
      <template #default="{ row, $index }">
        <el-button type="text" @click="handle(row, $index)">修改</el-button>
      </template>
    </el-table-column>
  </el-table>
</template>

<script setup lang="ts">
import { addDialog } from "@/components/gDialog"
import { reactive, shallowRef } from "vue"
import addUser from "./modal/addUser.vue"

interface IUser {
  name: string
  age: string
}

interface ITableI {
  data: IUser[]
}

const tableList: ITableI = reactive({ data: [] })
const handle = (row: any, index?: number) => {
  console.log(row)
  addDialog({
    title: "添加用户",
    component: shallowRef(addUser),
    width: "400px",
    props: {
      row
    },
    callBack: (isAdd: Boolean, data: IUser) => {
      if (isAdd) {
        addData(data)
      } else {
        tableList.data[index as number] = data
      }
      console.log(data, "data")
    }
  })
}
const addData = (data: IUser) => {
  tableList.data.push(data)
}
</script>

<style></style>

增加&修改

<template>
  <el-form ref="ruleFormRef" :inline="true" :rules="rules" :model="formInline" class="demo-form-inline">
    <el-form-item label="姓名" prop="name">
      <el-input v-model="formInline.name" placeholder="请输入名称" />
    </el-form-item>
    <el-form-item label="年龄" prop="age">
      <el-input v-model="formInline.age" placeholder="请输入年龄" />
    </el-form-item>
    <el-form-item>
      <el-button type="primary" @click="submitForm(ruleFormRef)">保存</el-button>
      <el-button @click="close">取消</el-button>
    </el-form-item>
  </el-form>
</template>

<script lang="ts" setup>
import { reactive, ref } from "vue"
import type { FormInstance, FormRules } from "element-plus"
const props = defineProps(["row"])

const formInline = reactive({
  name: props.row.name || "",
  age: props.row.age || ""
})

const emit = defineEmits(["close"])
const rules = reactive<FormRules>({
  name: [{ required: true, message: "请输入名称" }],
  age: [{ required: true, message: "请输入年龄" }]
})
const ruleFormRef = ref<FormInstance>()
const submitForm = async (formEl: FormInstance | undefined) => {
  if (!formEl) return
  await formEl.validate((valid, fields) => {
    if (valid) {
      console.log("submit!")
      const isAdd = props.row ? false : true
      emit("close", isAdd, formInline)
    } else {
      console.log("error submit!", fields)
    }
  })
}
const close = () => {}
</script>   

效果图

dialog.gif

新增需求-领导希望在点击菜单的时候弹窗隐藏再次点击回来的时候弹窗再次展开

效果图

dialog.gif

实现

主要实现思路是通过路由的path的变化在每次点开弹框的时候将path当作一个唯一值传入数组,再监听path的变化用v-show实现弹窗的隐藏和展示。有兴趣的小伙伴可以直接看源码。

附上代码

代码

Ending

❀完结撒花❀ 都看到这了 点个赞吧~