vue?ant?design?封裝彈窗表單的使用
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; // 彈出框?qū)挾? @Prop({type: String, default: '500px'}) width!: string; // 接收表單渲染內(nèi)容數(shù)據(jù) @Prop({type: Object, default: () => {console.log()}}) form!: {}; // 接收彈窗窗口標題 @Prop({type: String, default: '操作窗口'}) title!: string; // 接收表單渲染內(nèi)容格式 @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; } } // 監(jiān)聽表單渲染內(nèi)容數(shù)據(jù)接入 + 轉(zhuǎn)換多余傳入問題 @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) }) } // 監(jiān)聽是否彈窗屬性 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>實現(xiàn)的,一眼看去,應該就是通過插槽實現(xiàn)的,但是具體的實現(xiàn)過程是怎樣的,尚不清楚。
簡單調(diào)試了一下,如下圖所示。
ant-design-vue的Form組件的FormItem.js的部分源碼如下,
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
vue3+vite動態(tài)加載路由,本地路由和線上路由匹配方式
這篇文章主要介紹了vue3+vite動態(tài)加載路由,本地路由和線上路由匹配方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-06-06vue如何實現(xiàn)路由跳轉(zhuǎn)到外部鏈接界面
這篇文章主要介紹了vue如何實現(xiàn)路由跳轉(zhuǎn)到外部鏈接界面,具有很好的參考價值,希望對大家有所幫助。2022-10-10vue如何隨心所欲調(diào)整el-dialog中body的樣式
這篇文章主要介紹了vue如何隨心所欲調(diào)整el-dialog中body的樣式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-05-05