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-cli 创建模板项目
Nov 19 Vue.js
解决Vue-cli3没有vue.config.js文件夹及配置vue项目域名的问题
Dec 04 Vue.js
Vue实现点击当前行变色
Dec 14 Vue.js
详解Vue的异步更新实现原理
Dec 22 Vue.js
vue下拉刷新组件的开发及slot的使用详解
Dec 23 Vue.js
Vue实现省市区三级联动
Dec 27 Vue.js
vue中watch的用法汇总
Dec 28 Vue.js
vue项目如何监听localStorage或sessionStorage的变化
Jan 04 Vue.js
vue组件的路由高亮问题解决方法
May 11 Vue.js
springboot+VUE实现登录注册
May 27 Vue.js
vue项目配置sass及引入外部scss文件
Apr 14 Vue.js
el-table-column 内容不自动换行的解决方法
Aug 14 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实现ping
2006/10/09 PHP
PHP读取大文件的几种方法介绍
2016/10/27 PHP
php中简单的对称加密算法实现
2017/01/05 PHP
PHP里面把16进制的图片数据显示在html的img标签上(实现方法)
2017/05/02 PHP
PHP实现删除多重数组对象属性并重新赋值的方法
2017/06/07 PHP
什么是PHP7中的孤儿进程与僵尸进程
2019/04/14 PHP
laravel框架创建授权策略实例分析
2019/11/22 PHP
JavaScript 字符串乘法
2009/08/20 Javascript
读jQuery之十一 添加事件核心方法
2011/07/31 Javascript
JS实现的倒计时效果实例(2则实例)
2015/12/23 Javascript
JS组件中bootstrap multiselect两大组件较量
2016/01/26 Javascript
浅析Bootstrap组件之面板组件
2016/05/04 Javascript
利用node.js实现反向代理的方法详解
2017/07/24 Javascript
使用jQuery实现鼠标点击左右按钮滑动切换
2017/08/04 jQuery
Node.js静态服务器的实现方法
2018/02/28 Javascript
微信小程序之swiper轮播图中的图片自适应高度的方法
2018/04/23 Javascript
npm全局模块卸载及默认安装目录修改方法
2018/05/15 Javascript
vue 点击展开显示更多(点击收起部分隐藏)
2019/04/09 Javascript
[02:41]DOTA2亚洲邀请赛小组赛第三日 赛事回顾
2015/02/01 DOTA
400多行Python代码实现了一个FTP服务器
2012/05/10 Python
Python collections模块实例讲解
2014/04/07 Python
Python 3中print函数的使用方法总结
2017/08/08 Python
Python找出最小的K个数实例代码
2018/01/04 Python
python+selenium实现12306模拟登录的步骤
2021/01/21 Python
python 基于pygame实现俄罗斯方块
2021/03/02 Python
如何提高JDBC的性能
2013/04/30 面试题
C#基础面试题
2016/10/17 面试题
口腔医学技术应届生求职信
2013/11/09 职场文书
合作意向书范本
2014/03/31 职场文书
法人授权委托书格式
2014/04/08 职场文书
继承公证书
2014/04/09 职场文书
交通事故调解协议书
2014/04/16 职场文书
党员个人自我剖析材料
2014/10/08 职场文书
运动会1000米加油稿
2015/07/21 职场文书
保险公司2016开门红口号集锦
2015/12/24 职场文书
教你如何用cmd快速登录服务器
2022/06/10 Servers