vue3 + antd 封装动态表单组件(一)

本文介绍了如何在Vue3.3.11环境中利用AntDesignVue4.1.1创建动态表单组件,包括配置文件的编写、动态生成表单项以及处理数据验证和重置功能。
前置条件

vue版本 v3.3.11
ant-design-vue版本 v4.1.1

内容梗概

这篇文章的主要内容提供创建动态表单组件的基础模板,相关复杂功能可根据实际的业务场景自行实现。

先看效果图
在这里插入图片描述

代码区域

创建动态组件配置文件config.js

import { Input, Textarea, InputNumber, Select, RadioGroup, CheckboxGroup, DatePicker } from 'ant-design-vue';

// 表单域组件类型
export const componentsMap = {
    Text: Input,
    Textarea,
    Number: InputNumber,
    Select,
    Radio: RadioGroup,
    Checkbox: CheckboxGroup,
    DatePicker,
}

创建dynamic-form.vue组件

<template>
  <div>
    <a-form ref="formRef" :model="formModel" v-bind="$attrs">
      <a-form-item
        :name="item.field"
        :label="item.label"
        v-for="item in formSchema"
        :key="item.field"
        v-bind="item.formItemProps"
      >
        <component
          :is="componentsMap[item.component]"
          v-bind="item.componentProps"
          v-model:value="formModel[item.field]"
        />
      </a-form-item>
    </a-form>
  </div>
</template>

<script setup>
import { ref, watch, onMounted } from "vue";
import { componentsMap } from "./config.js";

const props = defineProps({
  // 表单项配置
  schema: {
    type: Array,
    default: () => [],
  },
  // 表单model配置,一般用于默认值、回显数据
  model: {
    type: Object,
    default: () => ({}),
  },
});

const formRef = ref(null);

const formSchema = ref([]);
const formModel = ref({});

// 表单初始化
const initForm = () => {
  formSchema.value = props.schema.map((x) => {
    return {
      ...x,
    };
  });

  // model初始数据
  formModel.value = props.schema.reduce((pre, cur) => {
    if (!pre[cur.field]) {
      // 表单初始数据(默认值)
      pre[cur.field] = cur.value;
      return pre;
    }
  }, {});
};

onMounted(() => {
  initForm();
  // 构建表单项后才回显model值,model会覆盖schema配置的value值
  watch(
    () => props.model,
    (newVal) => {
      formModel.value = { ...formModel.value, ...newVal };
    },
    {
      immediate: true,
      deep: true,
    }
  );
});

// 表单验证
const validateFields = () => {
  return new Promise((resolve, reject) => {
    formRef.value
      .validateFields()
      .then((formData) => {
        resolve(formData);
      })
      .catch((err) => reject(err));
  });
};

// 表单重置
const resetFields = (isInit = true) => {
  // 是否清空默认值
  if (isInit) {
    formModel.value = {};
  }
  formRef.value.resetFields();
};

// 暴露方法
defineExpose({
  validateFields,
  resetFields,
});
</script>

使用dynamic-form.vue组件

<template>
  <div style="padding: 200px">
    <DynamicForm ref="formRef" :schema="schema" :model="model" :labelCol="{ span: 4 }" :wrapperCol="{ span: 20 }"/>
    <div style="display: flex; justify-content: center">
      <a-button @click="handleReset(true)">重置(全部清空)</a-button>
      <a-button style="margin-left: 50px" @click="handleReset(false)"
        >重置</a-button
      >
      <a-button type="primary" style="margin-left: 50px" @click="handleSubmit"
        >提交</a-button
      >
    </div>
  </div>
</template>

<script setup>
import DynamicForm from "@/components/form/dynamic-form.vue";
import { ref } from "vue";
import dayjs from "dayjs";
const formRef = ref(null);

const schema = ref([
  {
    label: "姓名",
    field: "name",
    component: "Text",
    componentProps: {
      allowClear: true,
      showCount: true,
      maxlength: 20,
      style: {
        width: "500px",
      },
    },
    formItemProps: {
      rules: [
        {
          required: true,
          message: "请输入姓名",
          trigger: "blur",
        },
      ],
    },
  },
  {
    label: "性别",
    field: "sex",
    component: "Radio",
    componentProps: {
      options: [
        { value: 1, label: "男" },
        { value: 2, label: "女" },
        { value: 3, label: "保密" },
      ],
    },
    formItemProps: {
      rules: [
        {
          required: true,
          message: "请选择性别",
          trigger: "blur",
        },
      ],
    },
    value: 1,
  },
  {
    label: "生日",
    field: "birthday",
    component: "DatePicker",
    formItemProps: {
      rules: [
        {
          required: true,
          message: "生日日期不能为空",
          trigger: "blur",
        },
      ],
    },
  },
  {
    label: "兴趣",
    field: "hobby",
    component: "Checkbox",
    componentProps: {
      options: [
        { value: 1, label: "足球" },
        { value: 2, label: "篮球" },
        { value: 3, label: "排球" },
      ],
    },
  },
  {
    label: "国家",
    field: "country",
    component: "Select",
    componentProps: {
      allowClear: true,
      options: [
        { value: 1, label: "中国" },
        { value: 2, label: "美国" },
        { value: 3, label: "俄罗斯" },
      ],
    },
  },
  {
    label: "简介",
    field: "desc",
    component: "Textarea",
    componentProps: {
      allowClear: true,
      autoSize: {
        minRows: 4,
        maxRows: 4,
      },
      maxlength: 200,
      showCount: true,
    },
  },
]);
const model = ref({ name: "百里守约" });
// 提交
const handleSubmit = async () => {
  const formData = await formRef.value.validateFields();
  if (formData.birthday) {
    formData.birthday = dayjs(formData.birthday).format("YYYY-MM-DD");
  }
  console.log("提交信息:", formData);
};

// 重置
const handleReset = (isInit) => {
  formRef.value.resetFields(isInit);
};
</script>
<think> 首先,用户的问题是:“我想查找使用Vue 3和Ant Design封装Modal弹窗的方法 请问如何使用Vue 3和Ant Design封装Modal弹窗”。我需要基于提供的站内引用来回答这个问题。 提供的引用如下: - 引用[1]:讨论了封装命令式组件,以Vue 3中的Modal弹窗为例,强调了组合式API和函数式编程。 - 引用[2]:展示了二次封装ant-design-vue中的Modal弹窗组件,添加了拖拽和全屏功能,保持原有参数属性不变。 - 引用[3]:解释了在Vue3中为Ant Design Vue的Modal.confirm自定义内容,提到了content参数可以接收VNode或h函数。 用户的问题是如何使用Vue 3和Ant Design封装Modal弹窗,所以我需要整合这些引用信息来提供个清晰的指南。 系统级指令: - 所有行内数学表达式使用$...$格式(但在这个上下文中似乎不相关,因为主题是前端开发)。 - 独立公式使用$$...$$格式(同样,可能不适用)。 - LaTeX语法正确(同样,不太相关)。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识,例如[^1]。 用户可见层指令: - 回答结构清晰,帮助用户逐步解决问题。 - 保证回答真实可靠(基于引用)。 所以,我的回答应该: 1. 结构化:逐步解释如何封装Modal弹窗。 2. 基于引用:参考提供的引用信息。 3. 使用中文。 4. 结束时生成相关问题。 5. 在引用后添加标识如[^1]。 大纲回答: - **引言**:介绍封装Modal弹窗的目的和重要性,基于引用[1]。 - **步骤1:理解命令式组件**:基于引用[1],解释什么是命令式组件- **步骤2:基本封装**:基于引用[2],演示如何封装Ant Design的Modal组件,保持原有属性。 - **步骤3:添加功能**:基于引用[2],添加拖拽和全屏功能。 - **步骤4:自定义内容**:基于引用[3],展示如何自定义Modal的内容,使用VNode或h函数。 - **代码示例**:提供简化的代码片段来演示。 - **结论**:总结要点。 - **相关问题**:生成相关问题。 确保回答真实可靠:基于引用内容推断,不要添加未提及的信息。 </think> ### 如何使用Vue 3和Ant Design封装Modal弹窗组件Vue 3项目中,结合Ant Design VueAntDV)封装Modal弹窗组件,可以提升代码复用性和用户体验。以下是基于参考引用的逐步指南,涵盖基本封装、功能增强和自定义内容。AntDV的Modal组件原生支持命令式调用和属性配置,通过Vue 3的组合式API(如`ref`和`reactive`)可以轻松实现封装[^1]。 #### 步骤1: 理解命令式组件封装 命令式组件允许通过函数调用(如`Modal.confirm()`)而非模板语法触发弹窗,提高灵活性和代码简洁性。在Vue 3中,这得益于组合式API的函数式编程能力[^1]。核心思路是: - 继承AntDV的Modal组件- 保持原有属性(如`title`和`visible`),确保兼容性。 - 使用`defineComponent`创建可复用的组件。 #### 步骤2: 基本封装Modal组件 基于引用[2],创建个基础Modal封装组件,保留AntDV的所有参数属性。以下是简化的代码示例: ```vue <template> <a-modal v-bind="$attrs" <!-- 继承所有原生属性 --> @cancel="handleCancel" @ok="handleOk" > <slot></slot> <!-- 支持插槽内容 --> </a-modal> </template> <script lang="ts"> import { defineComponent, ref } from &#39;vue&#39;; import { Modal as AModal } from &#39;ant-design-vue&#39;; export default defineComponent({ name: &#39;CustomModal&#39;, components: { AModal }, emits: [&#39;cancel&#39;, &#39;ok&#39;], // 定义事件 setup(_, { emit }) { const handleCancel = () => { emit(&#39;cancel&#39;); }; const handleOk = () => { emit(&#39;ok&#39;); }; return { handleCancel, handleOk }; } }); </script> ``` - **说明**:使用`v-bind="$attrs"`继承所有AntDV Modal属性(如`title`或`visible`),确保原有功能不变[^2]。事件通过`emits`处理,用户可通过插槽注入内容。 #### 步骤3: 添加高级功能(拖拽和全屏) 引用[2]展示了如何扩展功能,如拖拽和全屏,不影响原生属性。以下是关键实现: - **拖拽功能**:使用Vue 3的`ref`绑定DOM元素,结合鼠标事件实现。 - **全屏功能**:通过动态CSS类控制弹窗尺寸。 示例代码(部分关键逻辑): ```vue <template> <a-modal ref="modalRef" v-bind="$attrs" :class="{ &#39;full-screen&#39;: isFullScreen }" @cancel="handleCancel" > <!-- 添加自定义控制按钮 --> <template #title> {{ title }} <button @click="toggleFullScreen">全屏</button> </template> <slot></slot> </a-modal> </template> <script lang="ts"> import { defineComponent, ref } from &#39;vue&#39;; export default defineComponent({ setup() { const isFullScreen = ref(false); const modalRef = ref(null); // 全屏切换 const toggleFullScreen = () => { isFullScreen.value = !isFullScreen.value; }; // 拖拽实现(简化示例) const handleDragStart = (e) => { // 使用鼠标事件计算位置偏移 e.preventDefault(); // 实现拖拽逻辑(需添加事件监听器) }; return { isFullScreen, toggleFullScreen, handleDragStart, modalRef }; } }); </script> ``` - **注意事项**:拖拽功能需在`mounted`生命周期添加事件监听;全屏通过CSS类(如`.full-screen { width: 100vw; height: 100vh; }`)控制。保持AntDV属性不变,确保无缝集成[^2]。 #### 步骤4: 自定义Modal内容 引用[3]强调,AntDV的`Modal.method()`(如`Modal.confirm`)支持高级自定义内容。使用VNode或渲染函数(h),实现复杂交互: - **VNode形式**:直接传递Vue组件- **h函数**:在setup中使用`h()`创建动态内容。 示例:命令式调用自定义Modal ```javascript import { h, ref } from &#39;vue&#39;; import { Modal } from &#39;ant-design-vue&#39;; // 自定义内容组件 const CustomContent = { setup() { const inputValue = ref(&#39;&#39;); return () => h(&#39;div&#39;, [ h(&#39;input&#39;, { modelValue: inputValue.value, onInput: (e) => inputValue.value = e.target.value, placeholder: &#39;输入内容&#39; }) ]); } }; // 命令式弹窗调用 function showCustomModal() { Modal.confirm({ title: &#39;自定义弹窗&#39;, content: h(CustomContent), // 使用h函数注入VNode onOk: () => console.log(&#39;确认操作&#39;) }); } ``` - **优势**:这样封装后,Modal可以承载表单、图表等动态内容,提升交互性[^3]。 #### 最佳实践总结 - **复用性原则**:封装时保留所有AntDV属性,避免破坏原有API。 - **性能优化**:在Vue 3中使用`ref`和`computed`管理状态,减少不必要的渲染。 - **场景适用**:命令式封装适用于全局弹窗(如确认框),而组件式适合局部复用。 通过以上步骤,您可以高效封装高性能、可定制的Modal弹窗组件[^1][^2][^3]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值