vue ant design 封装弹窗表单的使用


Posted in Vue.js onJune 01, 2022

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>

部分效果图:

vue ant design 封装弹窗表单的使用

使用ant-design-vue的Form表单

使用脚手架新建项目

vue create antd-demo

vue ant design 封装弹窗表单的使用

所以,得到了这么一个项目,如下

vue ant design 封装弹窗表单的使用

安装并导入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启动应用,效果如下

vue ant design 封装弹窗表单的使用

好了,应用就暂时介绍到这里。其实,我更想说说我的疑惑:

Hello.vue中,Username输入框的前面有个图片前缀,Password输入框的前面也有一个图片前缀,都是通过<template #prefix></template>实现的,一眼看去,应该就是通过插槽实现的,但是具体的实现过程是怎样的,尚不清楚。

简单调试了一下,如下图所示。

vue ant design 封装弹窗表单的使用

ant-design-vue的Form组件的FormItem.js的部分源码如下,

vue ant design 封装弹窗表单的使用

以上为个人经验,希望能给大家一个参考,也希望大家多多支持三水点靠木。


Tags in this post...

Vue.js 相关文章推荐
详解vue 组件注册
Nov 20 Vue.js
vue组件中节流函数的失效的原因和解决方法
Dec 02 Vue.js
vue使用echarts图表自适应的几种解决方案
Dec 04 Vue.js
vue中activated的用法
Jan 03 Vue.js
vue form表单post请求结合Servlet实现文件上传功能
Jan 22 Vue.js
Vue使用Ref跨层级获取组件的步骤
Jan 25 Vue.js
Vue 事件的$event参数=事件的值案例
Jan 29 Vue.js
vue 实现click同时传入事件对象和自定义参数
Jan 29 Vue.js
vue 项目@change多个参数传值多个事件的操作
Jan 29 Vue.js
vue-cli中实现响应式布局的方法
Mar 02 Vue.js
vue路由实现登录拦截
Mar 24 Vue.js
前端vue+express实现文件的上传下载示例
Feb 18 Vue.js
vue实现登陆页面开发实践
May 30 #Vue.js
vue router 动态路由清除方式
May 25 #Vue.js
vue如何清除浏览器历史栈
May 25 #Vue.js
vue3不同环境下实现配置代理
May 25 #Vue.js
vue使用element-ui按需引入
May 20 #Vue.js
vue/cli 配置动态代理无需重启服务的方法
May 20 #Vue.js
Vue ECharts实现机舱座位选择展示功能
May 15 #Vue.js
You might like
浅谈web上存漏洞及原理分析、防范方法(文件名检测漏洞)
2013/06/29 PHP
PHP开发工具ZendStudio下Xdebug工具使用说明详解
2013/11/11 PHP
为PHP5.4开启Zend OPCode缓存
2014/12/26 PHP
PHP实现自动对图片进行滚动显示的方法
2015/03/12 PHP
PHP实现获取第一个中文首字母并进行排序的方法
2017/05/09 PHP
jQuery.extend 函数详解
2012/02/03 Javascript
jQuery实现的一个tab切换效果内部还嵌有切换
2014/08/10 Javascript
ajax读取数据后使用jqchart显示图表的方法
2015/06/10 Javascript
JavaScript简单实现弹出拖拽窗口(二)
2016/06/17 Javascript
详解Vue CLI3 多页应用实践和源码设计
2018/08/30 Javascript
JavaScript学习笔记之DOM操作实例分析
2019/01/08 Javascript
解决vue跨域axios异步通信问题
2019/04/17 Javascript
在python的WEB框架Flask中使用多个配置文件的解决方法
2014/04/18 Python
Python内建模块struct实例详解
2018/02/02 Python
将Python字符串生成PDF的实例代码详解
2019/05/17 Python
Python3+Appium安装使用教程
2019/07/05 Python
python通过http下载文件的方法详解
2019/07/26 Python
给我一面国旗 python帮你实现
2019/09/30 Python
PyCharm 2019.3发布增加了新功能一览
2019/12/08 Python
python时间日期操作方法实例小结
2020/02/06 Python
学python最电脑配置有要求么
2020/07/05 Python
美国网上订购鲜花:FTD
2016/09/23 全球购物
Laura Mercier官网:彩妆大师罗拉玛斯亚的化妆品牌
2018/01/04 全球购物
意大利巧克力店:Chocolate Shop
2019/07/24 全球购物
法学函授自我鉴定
2014/02/06 职场文书
渔夫的故事教学反思
2014/02/14 职场文书
办公设备采购方案
2014/03/16 职场文书
2014年纪检监察工作总结
2014/11/11 职场文书
优秀高中学生评语
2014/12/30 职场文书
2015年复活节活动总结
2015/02/27 职场文书
法律意见书范文
2015/06/04 职场文书
医者仁心观后感
2015/06/17 职场文书
2016年119消防宣传日活动总结
2016/04/05 职场文书
年终工作总结范文
2019/06/20 职场文书
2019年汽车租赁合同范本!
2019/08/12 职场文书
详解Vue的列表渲染
2021/11/20 Vue.js