基于 Arco Design UI 封装的 Modal 组件

在日常开发中,Modal 是一个使用频率非常高的 UI 组件。通过封装,我们可以让它更灵活、更便捷地与业务逻辑结合。基于 这篇文章 的启发,我编写了一个基于 Arco Design UI 的 Modal 组件封装。以下是封装的源码及其设计思路。

功能简介

我们封装了一个 modal 函数,能够:

  1. 动态传入内容组件和组件属性;
  2. 支持自定义配置(如标题、按钮文案等);
  3. 自动管理 Modal 的打开、关闭,以及异步状态(如 confirmLoading)。
    通过封装,开发者只需调用一个函数,即可快速创建一个带有逻辑控制的弹窗,避免重复性代码。

源码实现

以下是完整源码,包含核心逻辑和注释。

js 复制代码
// modal.ts
import {
  createVNode,
  render as vueRender,
  DefineComponent,
  PropType,
} from "vue";
import { Modal as ArcoModal, ModalConfig } from "@arco-design/web-vue";

// 扩展 ModalConfig 类型,添加 confirmLoading 属性
interface ExtendedModalConfig extends ModalConfig {
  confirmLoading?: boolean;
}

/**
 * 创建一个动态 Modal 的封装
 * @param {DefineComponent} content - 要显示在 Modal 中的内容组件
 * @param {Record<string, any>} props - 传递给内容组件的属性
 * @param {Omit<ExtendedModalConfig, 'visible'>} config - Modal 的配置选项,排除 'visible'
 */
export default function modal<T extends DefineComponent>(
  content: T,
  props: InstanceType<T>["$props"],
  config: Omit<ExtendedModalConfig, "visible">
) {
  const container = document.createElement("div");
  const _contentVnode = createVNode(content, props);
  const metadata = Object.create({
    okText: "确定",
    cancelText: "取消",
    visible: true,
    ...config,
  });

  // 关闭弹窗逻辑
  function close() {
    metadata.visible = false;
    update();
    setTimeout(() => vueRender(null, container), 300);
  }

  // 更新弹窗配置
  function update(newConfig?: Partial<ExtendedModalConfig>) {
    Object.assign(metadata, newConfig);
    vueRender(
      createVNode(ArcoModal, metadata, {
        default: () => _contentVnode,
      }),
      container
    );
  }

  metadata.onCancel = async function (...arg: any[]) {
    await config.onCancel?.(...arg);
    close();
  };

  metadata.onOk = async function () {
    if (typeof config.onOk !== "function") {
      close();
      return;
    }

    const result = config.onOk() as Promise<any> | unknown;

    if (!(result instanceof Promise)) {
      close();
      return;
    }

    update({ confirmLoading: true });
    return result
      .then(() => {
        update({ confirmLoading: false });
        close();
      })
      .catch(() => {
        update({ confirmLoading: false });
      });
  };

  // 初次渲染
  update();

  document.body.appendChild(container);
}

核心设计思路

  1. 动态创建容器

    我们通过 document.createElement('div') 创建了一个 DOM 容器,并在 document.body 中插入,以便动态渲染 Modal。

  2. 使用 createVNode 和 vueRender

    createVNode 用于生成内容组件的虚拟节点;

    vueRender 负责将 Modal 渲染到指定的容器中。

  3. 配置属性的动态更新

    将 ArcoModal 的所有配置存储在 metadata 中;

    通过 update 函数,可以动态更新配置,比如设置 confirmLoading 状态。

  4. 异步操作的处理

    如果 onOk 返回一个 Promise,则在弹窗确认按钮点击时,显示 loading 状态;

    确认完成后,自动关闭弹窗。

    使用方式

  5. 创建一个内容组件

js 复制代码
// MyContentComponent.ts
import { defineComponent } from "vue";

export default defineComponent({
  name: "MyContentComponent",
  props: {
    title: String,
  },
  setup(props) {
    return () => <div>标题:{props.title}</div>;
  },
});
7. 调用封装的 modal 函数
import modal from "./modal";
import MyContentComponent from "./MyContentComponent";

modal(MyContentComponent, { title: "动态弹窗" }, {
  title: "这是一个标题",
  onOk: () => {
    return new Promise((resolve) => {
      setTimeout(() => {
        console.log("确认操作完成");
        resolve(true);
      }, 2000);
    });
  },
  onCancel: () => {
    console.log("取消操作");
  },
});
  1. 效果展示
  • 弹窗内容为 MyContentComponent;
  • 点击确认时,显示加载状态(2 秒后关闭弹窗);
  • 点击取消时,仅关闭弹窗。
    优化点和未来改进
  • 事件监听清理:弹窗关闭后,应清理挂载的 DOM 节点,防止内存泄漏。
    更加灵活的样式支持:可以通过额外的 class 或 style 配置,定制化样式。
    多实例支持:扩展当前实现,使其支持多个弹窗同时存在。
    通过以上封装,我们不仅提升了开发效率,也在代码结构上保持了高度的模块化和可读性。这种方法可以灵活地集成到实际业务中,希望对你有所帮助! 😊
相关推荐
m0_7482352435 分钟前
前端:HTML、CSS、JS、Vue
前端·javascript·html
一朵好运莲1 小时前
HBuilderX(uni-app)Vue3路由传参和接收路由参数!!
前端·vue.js·uni-app
Charonmomo1 小时前
React - echarts 世界地图,中国地图绘制
javascript·react.js·echarts
qq_10613834571 小时前
快速搭建SpringBoot3+Vue3+ElementPlus管理系统
java·vue.js·spring boot·idea
灵性(๑>ڡ<)☆1 小时前
智慧商城项目2(vue核心技术与实战)
前端·javascript·vue.js
没资格抱怨2 小时前
分配角色业务
javascript·vue.js·elementui
小政爱学习!2 小时前
点击按钮打开dialog嵌套表格checked数据关闭dialog回显checked数据
javascript·vue.js·elementui
一周七喜h2 小时前
vue2 el-table实现跨页多选功能
javascript·vue.js·elementui
土坷垃2 小时前
echarts可视化之起点归零+左右0轴对齐
javascript·echarts
摸鲨鱼的脚2 小时前
Vue导出报表功能【动态表头+动态列】
前端·javascript·vue.js