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的tree组件
Dec 03 Vue.js
Vue实现指令式动态追加小球动画组件的步骤
Dec 18 Vue.js
Vue 简单实现前端权限控制的示例
Dec 25 Vue.js
vue中封装axios并实现api接口的统一管理
Dec 25 Vue.js
vue+element UI实现树形表格
Dec 29 Vue.js
vuex的使用和简易实现
Jan 07 Vue.js
vue-resource 拦截器interceptors使用详解
Jan 18 Vue.js
vue实现验证用户名是否可用
Jan 20 Vue.js
Vue 集成 PDF.js 实现 PDF 预览和添加水印的步骤
Jan 22 Vue.js
vue+echarts实现中国地图流动效果(步骤详解)
Jan 27 Vue.js
使用vue-element-admin框架从后端动态获取菜单功能的实现
Apr 29 Vue.js
一定要知道的 25 个 Vue 技巧
Nov 02 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
ThinkPHP关联模型操作实例分析
2012/09/23 PHP
PHP多线程类及用法实例
2014/12/03 PHP
基于ThinkPHP实现批量删除
2015/12/18 PHP
Zend Framework动作控制器用法示例
2016/12/09 PHP
风吟的小型JavaScirpt库 (FY.JS).
2010/03/09 Javascript
JQuery扩展插件Validate 1 基本使用方法并打包下载
2011/09/05 Javascript
再谈javascript面向对象编程
2012/03/18 Javascript
jquery实现的网页自动播放声音
2014/04/30 Javascript
jquery比较简洁的软键盘特效实现方法
2015/03/19 Javascript
Javascript基于对象三大特性(封装性、继承性、多态性)
2016/01/04 Javascript
jQuery Easyui Tabs扩展根据自定义属性打开页签
2016/08/15 Javascript
浅谈Node.js:fs文件系统模块
2016/12/08 Javascript
ES6字符串模板,剩余参数,默认参数功能与用法示例
2017/04/06 Javascript
Angularjs中数据绑定的实例详解
2017/08/25 Javascript
详解在WebStorm中添加Vue.js单文件组件的高亮及语法支持
2017/10/21 Javascript
全面解析vue router 基本使用(动态路由,嵌套路由)
2018/09/02 Javascript
详解Vue SSR( Vue2 + Koa2 + Webpack4)配置指南
2018/11/13 Javascript
elementUI中Table表格问题的解决方法
2018/12/04 Javascript
详解微信小程序获取当前时间及日期的方法
2019/04/28 Javascript
AngularJs的$http发送POST请求,php无法接收Post的数据问题及解决方案
2020/08/13 Javascript
react+antd 递归实现树状目录操作
2020/11/02 Javascript
Python利用IPython提高开发效率
2016/08/10 Python
Python实现简单的多任务mysql转xml的方法
2017/02/08 Python
Python requests发送post请求的一些疑点
2018/05/20 Python
Python实现模拟浏览器请求及会话保持操作示例
2018/07/30 Python
使用matlab 判断两个矩阵是否相等的实例
2020/05/11 Python
Python实现手势识别
2020/10/21 Python
python安装sklearn模块的方法详解
2020/11/28 Python
CSS3,线性渐变(linear-gradient)的使用总结
2017/01/09 HTML / CSS
PHP如何去执行一个SQL语句
2016/03/05 面试题
统计学专业毕业生的自我评价分享
2013/11/28 职场文书
小学新教师培训方案
2014/02/03 职场文书
亲属关系公证书
2014/04/08 职场文书
司法局群众路线教育实践活动整改措施
2014/09/17 职场文书
装饰施工员岗位职责
2015/04/11 职场文书
2016计划生育先进个人事迹材料
2016/02/29 职场文书