本文以封装一个旋转的 Loading 图标为例,讲解如何自定义 Message 动态(这里的“动态”的意思是"会动的")图标。

Element-Plus Message 提供了一个icon属性,用来自定义图标。icon属性接受Component或者String类型的参数。这个时候,就会想到可以引入 Element-Plus 自带的图标组件:

...
import { Loading } from '@element-plus/icons-vue'

ElMessage({
    icon:  Loading,
    ...
});
...

可惜上面这个图标不会动。

Element-Plus 提供了is-loadingCSS Class,只要添加在el-icon组件上,就能够这个组件旋转起来。
那么我们自己封装旋转的el-icon成一个自定义组件,就能够让 Message 的图标动起来了:

//新建组件loading.vue
<script setup>
import { Loading } from '@element-plus/icons-vue';
</script>

<template>
    <el-icon class="is-loading"><Loading /></el-icon>
</template>

使用上面的组件:

//javascript
import MyLoading from 'loading.vue';

ElMessage({
    icon:  MyLoading,
    ...
});

这样就实现了目标,不足就是需要建立一个组件文件。

如果不希望为了实现旋转效果而封装一个自定义组件,还有其他办法可以实现目标。
查看 Message 的源代码(位置在你的项目\node_modules\element-plus\es\utils\vue\icon.mjs),可以发现,Message 的icon属性可以接受 3 种类型:

const iconPropType = definePropType([
  String,
  Object,
  Function
]);

String 接受一个不能有特殊字符的字符串,经过 Vue 的 CreateNode 函数处理,会在页面中生成一个以这个字符串命名的标签,例如:

ElMessage({
    icon:  'MyTag',
    ...
});

会在图标应该出现的位置生成一个自定义 HTML 标签:

<MyTag></MyTag>

但暂时没有找到为它添加属性的方法。如果有明白的同学,欢迎通过站内联系方式告诉我,感谢!

最后,还可以传递 Function 类型。Element Message 文档中提到的icon属性可以是Component,也就是组件。组件可以是一个符合 Vue 组件格式的 JavaScript 对象,也可以是一个函数。前面的文章(戳这里)讲到了函数式组件,这种组件是一个函数。

所以,可以直接创建一个函数,该函数使用渲染函数h返回一个组件,最终代码如下:

import { Loading } from '@element-plus/icons-vue';

export const loading = ElMessage({
    ...
    icon: () => {
        return h(
            'div',
            //给任意 HTML 元素增加下面的两个 Class,都会应用旋转效果
            //这两个 Class 是 Element-Plus 提供的
            //这里这样用仅仅为了方便,也可以自己写动画
            { class: 'el-icon is-loading' },
            [h(Loading)]
        );
    },
    //不自动关闭,通过 Message 实例的 close 方法手动关闭
    duration: 0,
    ...
});

这样,一个会显示加载动画的自定义 Message 就实现了。下面是完整的代码,还顺便封装成了一个更方便的工具:

//新建 msg.js
import { h } from 'vue';
import { ElMessage } from "element-plus";
import { Loading } from "@element-plus/icons-vue";

//可以在全局样式中修改 Message 的样式
const customClass = 'el-message-custom';

const duration = 4000;
function msg(message, type = 'message') {
    if (type === 'loading') {
        return ElMessage({
            icon: () => {
                return h(
                    'div',
                    { class: 'el-icon is-loading' },
                    [h(Loading)]
                );
            },
            message,
            customClass,
            duration: 0,
        });
    }

    ElMessage({ message, type, customClass, duration });
}

export default {
    message: message => {
        msg(message);
    },
    success: message => {
        msg(message, 'success');
    },
    warning: message => {
        msg(message, 'warning');
    },
    error: message => {
        msg(message, 'error');
    },
    loading: message => {
        return msg(message, 'loading');
    }
};

使用:

import msg from 'msg.js'

const loading = msg.loading('提交中,请稍候');
setTimeout(()=>{
    loading.close();
    msg.success('提交成功');
}, 3000)

End

标签: vue3, element-plus