基于Vue 3和Element Plus实现简单的钩子函数管理各类弹窗操作

1、钩子函数useModal

(1)利用h渲染函数,可以给弹窗内容添加样式,之所以不用html模板字符串,因为模板字符串需要v-html,它是一个危险的用法,可能导致xss攻击:

Although message property supports HTML strings, dynamically rendering arbitrary HTML on your website can be very dangerous because it can easily lead to XSS attacks. So when dangerouslyUseHTMLString is on, please make sure the content of message is trusted, and never assign message to user-provided content.

(2)先让弹窗隐藏,再利用nextTick让弹窗显示,更好地强制弹窗开启

(3)根据弹窗类型和状态判断调用什么接口

(4)适用于比较复杂的弹窗功能,如果只是简单的弹窗(确认框,alert框等),使用element-plus的ElMessageBox即可(其他框架也有类似的组件)

复制代码
import { InfoFilled, QuestionFilled } from '@element-plus/icons-vue'
import { get } from 'lodash-es'
import { computed, h, nextTick, ref } from 'vue'
import axios from 'axios'
import OtherContentComponent from './OtherContentComponent.vue' // 其他要显示在弹窗中的自定义组件

const useModal = () => {
  const modalShow = ref<boolean>(false)
  const showStatus = ref<string>('add')
  const modalContent = ref<any>('')
  const modalTitle = ref<string>('')
  const statusFlag = ref<string>('')
  const modalType = ref<string>('info')
  const seletedItem = ref<any>({})

  const getModalContent = (content: string) => {
    return h('p', { style: 'display:flex;align-items:center;' }, [
      h(
        InfoFilled,
        {
          style: {
            display: modalType.value === 'info' ? 'inline-block' : 'none',
            width: '20px',
            height: '20px',
            'margin-right': '5px',
            color: 'blue'
          }
        },
        {}
      ),
      h(
        QuestionFilled,
        {
          style: {
            display: modalType.value === 'warning' ? 'inline-block' : 'none',
            width: '20px',
            height: '20px',
            'margin-right': '5px',
            color: 'red'
          }
        },
        {}
      ),
      h('span', null, content)
    ])
  }

  const computedModalContent = computed(() => {
    if(modalContent.value === 'other') {
      // OtherContentComponent组件需要的参数和事件(请根据组件具体情况而定,下方仅为展示逻辑而写)
      const OtherContentParams = {initFormData: seletedItem.value, onClick: () => {
        // do sth
      }}
      return h(OtherContentComponent, OtherContentParams, { default: () => {} })
    }
    if (typeof modalContent.value === 'string') {
      return getModalContent(modalContent.value)
    }
    return modalContent.value
  })

  const onChangeStatusModal = (seletedItem: any, statusName: string) => {
    modalShow.value = false
    modalType.value = 'info'
    seletedItem.value = seletedItem // 存下参数,方便编辑类弹窗回显数据,或者提交函数调用接口使用(也可以换成pinia全局存储)
    nextTick(() => {
      modalTitle.value = '确认提示'
      modalShow.value = true
      showStatus.value = 'default'
      statusFlag.value = statusName
      if (statusFlag.value === 'delete') {
        modalType.value = 'warning'
        modalContent.value = h('div', null, [
          h('p', null, getModalContent(`确认要删除 模板 ${seletedItem.modelName}?`)),
          h(
            'p',
            { style: 'color: red;margin-left:25px;' },
            '模板删除后,将无法在名单中选择!'
          )
        ])
      } else if (statusFlag.value === 'set-default') {
        modalType.value = 'warning'
        modalContent.value = `确认要将 模板 ${seletedItem.modelName} 设置为默认模板?`
      } else {
        modalContent.value = 'other'
      }
    })
  }

  const submitEditModal = (params: any, showStatus: string) => {
    // 普通确认框一般使用存储的变量seletedItem.value的值就够了,复杂的弹窗需要使用弹窗(比如新增输入或者编辑输入弹窗)自身发送过来的params的值
    if (showStatus === 'default' && statusFlag.value === 'delete') {
      axios.post('/xxx', seletedItem.value).then(() => {
        // do sth
        modalShow.value = false
      })
      return
    } else if (showStatus === 'default' && statusFlag.value === 'set-default') {
      axios.post('/xxx', seletedItem.value).then(() => {
        // do sth
        modalShow.value = false
      })
      return
    } else if (showStatus === 'add' || showStatus === 'edit') {
      axios.post('/xxx', params).then(() => {
        // do sth
        modalShow.value = false
      })
      return
    }
    // do sth
    modalShow.value = false
  }

  return {
    modalTitle,
    modalContent: computedModalContent,
    onChangeStatusModal,
    modalShow,
    showStatus,
    submitEditModal
  }
}

export default useModal

2、弹窗组件简单示例

复制代码
<template>
  <el-dialog
    :model-value="visible"
    :width="500"
    @close="onClose"
  >
    <template #header>
      <div class="modal-title">{{ title }}</div>
    </template>
    <template #default>
      <component :is="content" />
    </template>
    <template #footer>
      <span class="my-footer">
        <el-button @click="onClose">取 消</el-button>
        <el-button type="primary" @click="submitForm">确 定</el-button>
      </span>
    </template>
  </el-dialog>
</template>
<script lang="ts" setup name="templateDialog">
import { toRefs } from 'vue'

const props = defineProps<{
  visible: boolean
  showStatus: string
  title?: string
  content?: any
}>()
const emit = defineEmits(['update:visible', 'onSubmit'])
const { visible, showStatus } = toRefs(props)

const onClose = () => {
  emit('update:visible', false)
}

const submitForm = () => {
  const params = {} // 接口需要的参数,视情况赋值
  emit('onSubmit', params, showStatus.value)
}
</script>

<style lang="less" scoped></style>

使用弹窗组件

复制代码
<template>
  <div>
    <div class="btns">
      <el-button type="primary" @click="onChangeStatusModal({id:111}, 'delete')">删除</el-button>
      <el-button type="primary" @click="onChangeStatusModal({id:222}, 'set-default')">设为默认模板</el-button>
    </div>
    <MyModal
      v-model:visible="modalShow"
      :showStatus="showStatus"
      @on-submit="submitEditModal"
      v-if="modalShow"
      :title="modalTitle"
      :content="modalContent"
    />
  </div>
</template>

<script setup lang="ts">
import MyModal from './MyModal.vue'
import useModal from './useModal'

const {
  modalShow,
  showStatus,
  onChangeStatusModal,
  modalTitle,
  modalContent,
  submitEditModal
} = useModal()
</script>

参考资料地址:

https://cn.vuejs.org/api/render-function.html#hhttps://cn.vuejs.org/api/render-function.html#h

https://element-plus.org/en-US/component/message-boxhttps://element-plus.org/en-US/component/message-box

https://element-plus.org/en-US/component/dialoghttps://element-plus.org/en-US/component/dialog

https://element-plus.org/en-US/component/iconhttps://element-plus.org/en-US/component/icon

相关推荐
程序员爱钓鱼1 分钟前
Node.js 编程实战:测试与调试 —— 日志与监控方案
前端·后端·node.js
计算机学姐7 分钟前
基于SpringBoot的汉服租赁系统【颜色尺码套装+个性化推荐算法+数据可视化统计】
java·vue.js·spring boot·后端·mysql·信息可视化·推荐算法
+VX:Fegn08958 分钟前
计算机毕业设计|基于springboot + vue建筑材料管理系统(源码+数据库+文档)
数据库·vue.js·spring boot·后端·课程设计
Mapmost10 分钟前
数字孪生项目效率翻倍!AI技术实测与场景验证实录
前端
小酒星小杜14 分钟前
在AI时代,技术人应该每天都要花两小时来构建一个自身的构建系统-Input篇
前端·程序员·架构
雪碧聊技术16 分钟前
ElementPlus徽章组件:展示日期面板每天未完成的待办数量
vue.js·日期选择器·elementplus·el-badge徽章组件
Cache技术分享22 分钟前
290. Java Stream API - 从文本文件的行创建 Stream
前端·后端
陈_杨24 分钟前
前端成功转鸿蒙开发者真实案例,教大家如何开发鸿蒙APP--ArkTS 卡片开发完全指南
前端·harmonyos
小杨同学4931 分钟前
C 语言实战:枚举类型实现数字转星期(输入 1~7 对应星期几)
前端·后端
陈_杨32 分钟前
前端成功转鸿蒙开发者真实案例,教大家如何开发鸿蒙APP--ArkTS 卡片刷新机制
前端·harmonyos