vue ant design 封装弹窗表单的使用
作者:采药人~陈大仙
这篇文章主要介绍了vue ant design 封装弹窗表单的使用,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
vue ant design 封装弹窗表单
<template> <div id="formForm"> <a-modal :visible="true" :title='title' @ok="handleOk('ok')" @cancel="handleOk('return')" :centered="true" :confirmLoading="confirmLoading" :width="width"> <a-form :form="formState" :label-col="{ span: 5 }" :wrapper-col="{ span: 17 }"> <div v-for="itme in formData" :key="itme.value" > <!-- 输入款 --> <a-form-item :label="itme.label" v-if="itme.type === 'input'" :label-col="{ span: itme.labelCol ? itme.labelCol : 5 }" :wrapper-col="{ span: itme.wrapper ? itme.wrapper : 17 }"> <a-input v-decorator="[itme.value, { rules: [{ required: itme.required?itme.required:false, message: itme.message?itme.message:' ' }, {validator: itme.validator}]}]" :placeholder="!itme.placeholder ? itme.label : itme.label" allowClear> <!-- 插入输入框的下拉框选择器 --> <a-select v-if="itme.select && itme.select.length>0" slot="addonBefore" v-decorator="[ itme.header ]" style="width: 90px"> <a-select-option v-for="select in itme.select" :key="select.value"> {{select.label}} </a-select-option> </a-select> </a-input> </a-form-item> <!-- 开始结束时间选择 --> <a-form-item :label="itme.label" v-if="itme.type === 'rangePicker'"> <a-range-picker :placeholder="!itme.placeholder ? itme.label : itme.placeholder" showTime :style="`width: ${!itme.wrapper?'320':itme.wrapper}px;`" v-decorator="[itme.value, { rules: [{ required: itme.required?itme.required:false, message: itme.message?itme.message:' ' }]}]" /> </a-form-item> <!-- 单个时间选择 --> <a-form-item :label="itme.label" v-if="itme.type === 'datePicker'"> <a-date-picker :style="`width: ${!itme.wrapper?'180':itme.wrapper}px;`" v-decorator="[ itme.value, { rules: [{ required: itme.required?itme.required:false, message: itme.message?itme.message:' ' }]}]" showTime :placeholder="!itme.placeholder ? itme.label : itme.placeholder" /> </a-form-item> <!-- 选择框 --> <a-form-item :label="itme.label" v-if="itme.type === 'select'" :label-col="{ span: itme.labelCol ? itme.labelCol : 5 }" :wrapper-col="{ span: itme.wrapper ? itme.wrapper : 8 }"> <a-select allowClear v-decorator="[ itme.value, { rules: [{ required: itme.required?itme.required:false, message: itme.message?itme.message:' ' }]}]" :placeholder="!itme.placeholder ? itme.label : itme.placeholder"> <a-select-option v-for="optionItme in itme.option" :key="optionItme.value"> {{optionItme.label}} </a-select-option> </a-select> </a-form-item> <!-- 单选框 --> <a-form-item :label="itme.label" v-if="itme.type === 'radio'"> <a-radio-group v-decorator="[ itme.value, { rules: [{ required: itme.required?itme.required:false, message: itme.message?itme.message:' ' }]}]"> <a-radio v-for="radioItme in itme.radio" :key="radioItme.value" :value="radioItme.value"> {{radioItme.label}} </a-radio> </a-radio-group> </a-form-item> <!-- 开关按钮 --> <a-form-item :label="itme.label" v-if="itme.type === 'switch'"> <a-switch v-decorator="[ itme.value, { valuePropName: 'checked' }]" /> </a-form-item> <!-- 图片上传 --> <a-form-item :label="itme.label" v-if="itme.type === 'upload'" :label-col="{ span: itme.labelCol ? itme.labelCol : 5 }" :wrapper-col="{ span: itme.wrapper ? itme.wrapper : 20 }"> <a-upload v-decorator="[ itme.value, { valuePropName: 'fileList', getValueFromEvent: normFile, }]" :action="itme.action?itme.action:'https://www.mocky.io/v2/5cc8019d300000980a055e76'" listType="picture-card" @preview="handlePreview"> <div v-if="itme.value.length < 8"> <a-icon type="plus" /> <div class="ant-upload-text">点击上传图片</div> </div> </a-upload> <a-modal :visible="previewVisible" :footer="null" @cancel="previewVisible = false"> <img alt="example" style="width: 100%" :src="previewImage" /> </a-modal> </a-form-item> </div> </a-form> </a-modal> </div> </template>
<script lang='ts'> import { Component, Vue, Prop, Emit, Watch } from 'vue-property-decorator'; import Moment from 'moment' function getBase64(file) { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.readAsDataURL(file); reader.onload = () => resolve(reader.result); reader.onerror = error => reject(error); }); } @Component({ data() { return { formState: this.$form.createForm(this), previewVisible: false, previewImage: '' }; }, }) export default class FormForm extends Vue { [x: string]: any; // 弹出框宽度 @Prop({type: String, default: '500px'}) width!: string; // 接收表单渲染内容数据 @Prop({type: Object, default: () => {console.log()}}) form!: {}; // 接收弹窗窗口标题 @Prop({type: String, default: '操作窗口'}) title!: string; // 接收表单渲染内容格式 @Prop({type: Array, default: () => []}) formData!: []; // 返出取消和确定按钮 @Emit('handleOk') handleOk(e) { if (e === 'return') { return 'true'; } else if (e === 'ok') { let stateType: object | boolean = false; this.formState.validateFields((err, value) => { if (!err) { this.confirmLoading = true; stateType = value; } }) return stateType; } } // 监听表单渲染内容数据接入 + 转换多余传入问题 @Watch('form', {immediate: true, deep: false}) onForm(e) { let obj: object = {}; Object.keys(e).forEach(key => { Array.from(this.formData).forEach((res: any | object) => { if (key === res.value || key === res.header) { if (res.type === 'rangePicker' && e[key].length > 0) { e[key] = [ Moment(e[key][0]), Moment(e[key][1]) ] } if (res.type === 'datePicker' && e[key]) { e[key] = Moment(e[key]) } obj[key] = e[key] } }) }) this.$nextTick(() => { this.formState.setFieldsValue(obj) }) } // 监听是否弹窗属性 public visibleOff: boolean = false; // 确定按钮loading public confirmLoading: boolean = false; // --------- methods ------------ async handlePreview(file) { if (!file.url && !file.preview) { file.preview = await getBase64(file.originFileObj); } this.previewImage = file.url || file.preview; this.previewVisible = true; } normFile(e) { if (Array.isArray(e)) { return e; } return e && e.fileList; } } </script>
<style lang='scss' scpoed> .ant-form-item-label{ white-space: pre-wrap; line-height: 25px; } .ant-row{ display: flex; align-items: center; } .ant-form{ max-height: 60vh; overflow: auto; &::-webkit-scrollbar { display: none;; } } .ant-form-item{ margin-bottom: 10px; } .ant-form-item-control{ left: 10px; max-height: 225px; overflow: auto; &::-webkit-scrollbar{ display: none; } } .ant-upload-select-picture-card i { font-size: 32px; color: #999; } .ant-upload-select-picture-card .ant-upload-text { margin-top: 8px; color: #666; } </style>
部分效果图:
使用ant-design-vue的Form表单
使用脚手架新建项目
vue create antd-demo
所以,得到了这么一个项目,如下
安装并导入ant-design-vue,使用Form组件
npm install --save ant-design-vue@next
修改main.ts
import { createApp } from 'vue'; import App from './App.vue'; import Antd from "ant-design-vue"; import "ant-design-vue/dist/antd.css"; createApp(App).use(Antd).mount('#app');
修改App.vue
<template> <HelloWorld/> </template>
<script lang="ts"> import { defineComponent } from 'vue'; import HelloWorld from './components/HelloWorld.vue'; export default defineComponent({ name: 'App', components: { HelloWorld } }); </script>
<style> #app { font-family: Avenir, Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; margin-top: 60px; } </style>
修改HelloWorld.vue
<template> <a-form layout="inline" :model="formState" @finish="handleFinish" @finishFailed="handleFinishFailed" > <a-form-item> <a-input v-model:value="formState.user" placeholder="Username"> <template #prefix><UserOutlined style="color: rgba(0, 0, 0, 0.25)" /></template> </a-input> </a-form-item> <a-form-item> <a-input v-model:value="formState.password" type="password" placeholder="Password"> <template #prefix><LockOutlined style="color: rgba(0, 0, 0, 0.25)" /></template> </a-input> </a-form-item> <a-form-item> <a-button type="primary" html-type="submit" :disabled="formState.user === '' || formState.password === ''" > Log in </a-button> </a-form-item> </a-form> </template>
<script lang="ts"> import { UserOutlined, LockOutlined } from '@ant-design/icons-vue'; import { ValidateErrorEntity } from 'ant-design-vue/es/form/interface'; import { defineComponent, reactive, UnwrapRef } from 'vue'; interface FormState { user: string; password: string; } export default defineComponent({ setup() { const formState: UnwrapRef<FormState> = reactive({ user: '', password: '', }); const handleFinish = (values: FormState) => { console.log(values, formState); }; const handleFinishFailed = (errors: ValidateErrorEntity<FormState>) => { console.log(errors); }; return { formState, handleFinish, handleFinishFailed, }; }, components: { UserOutlined, LockOutlined, }, }); </script>
启动应用,测试验证
npm run serve启动应用,效果如下
好了,应用就暂时介绍到这里。其实,我更想说说我的疑惑:
Hello.vue中,Username输入框的前面有个图片前缀,Password输入框的前面也有一个图片前缀,都是通过<template #prefix></template>实现的,一眼看去,应该就是通过插槽实现的,但是具体的实现过程是怎样的,尚不清楚。
简单调试了一下,如下图所示。
ant-design-vue的Form组件的FormItem.js的部分源码如下,
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。