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使用vant中的checkbox实现全选功能
Nov 17 Vue.js
Vue用mixin合并重复代码的实现
Nov 27 Vue.js
vue3.0自定义指令(drectives)知识点总结
Dec 27 Vue.js
vue自定义组件实现双向绑定
Jan 13 Vue.js
vue 递归组件的简单使用示例
Jan 14 Vue.js
vue实现轮播图帧率播放
Jan 26 Vue.js
用vite搭建vue3应用的实现方法
Feb 22 Vue.js
Vue通过懒加载提升页面响应速度
May 10 Vue.js
Vue3.0 手写放大镜效果
Jul 25 Vue.js
vue自定义右键菜单之全局实现
Apr 09 Vue.js
vue使用watch监听属性变化
Apr 30 Vue.js
Vue Element plus使用方法梳理
Dec 24 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
php md5下16位和32位的实现代码
2008/04/09 PHP
php下关于Cannot use a scalar value as an array的解决办法
2010/08/08 PHP
ThinkPHP模板引擎之导入资源文件方法详解
2014/06/18 PHP
smarty模板引擎之分配数据类型
2015/03/30 PHP
php中session_id()函数详细介绍,会话id生成过程及session id长度
2015/09/23 PHP
Jquery作者John Resig自己封装的javascript 常用函数
2009/11/09 Javascript
基于jQuery实现模拟页面加载进度条
2013/04/01 Javascript
JSON序列化与解析原生JS方法且IE6和chrome测试通过
2013/09/05 Javascript
javascript if条件判断方法小结
2014/05/17 Javascript
json中换行符的处理方法示例介绍
2014/06/10 Javascript
javascript实现起伏的水波背景效果
2016/05/16 Javascript
Javascript中的数组常用方法解析
2016/06/17 Javascript
AngularJS基础 ng-non-bindable 指令详细介绍
2016/08/02 Javascript
ES6概念 Symbol.keyFor()方法
2016/12/25 Javascript
AngularJS表格样式简单设置方法示例
2017/03/03 Javascript
无循环 JavaScript(map、reduce、filter和find)
2017/04/08 Javascript
Node.js Mongodb 密码特殊字符 @的解决方法
2017/04/11 Javascript
JS实现的点击按钮图片上下滚动效果示例
2019/01/28 Javascript
产制造追溯系统之通过微信小程序实现移动端报表平台
2019/06/03 Javascript
layui数据表格跨行自动合并的例子
2019/09/02 Javascript
JS实现的进制转换,浮点数相加,数字判断操作示例
2019/11/09 Javascript
JS快速实现简单计算器
2020/04/08 Javascript
微信小程序抽奖组件的使用步骤
2021/01/11 Javascript
[02:08]我的刀塔不可能这么可爱 胡晓桃_1
2014/06/20 DOTA
[57:59]EG vs Secret 2018国际邀请赛淘汰赛BO3 第一场 8.22
2018/08/23 DOTA
Django实现单用户登录的方法示例
2019/03/28 Python
对django的User模型和四种扩展/重写方法小结
2019/08/17 Python
HTML5 Canvas 破碎重组的视频特效的示例代码
2019/09/24 HTML / CSS
12个不为大家熟知的HTML5设计小技巧
2016/06/02 HTML / CSS
给物业的表扬信
2014/01/21 职场文书
地理教师岗位职责
2014/03/16 职场文书
中秋节活动总结
2014/08/29 职场文书
导师工作推荐信
2015/03/27 职场文书
酒店收银员岗位职责
2015/04/07 职场文书
农村房屋租赁合同(范本)
2019/07/23 职场文书
HTML5页面音频自动播放的实现方式
2021/06/21 HTML / CSS