亲宝软件园·资讯

展开

vue ant design封装弹窗表单

采药人~陈大仙 人气:0

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的部分源码如下,

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

加载全部内容

相关教程
猜你喜欢
用户评论