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>
<!-- 開(kāi)始結(jié)束時(shí)間選擇 -->
<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>
<!-- 單個(gè)時(shí)間選擇 -->
<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>
<!-- 開(kāi)關(guān)按鈕 -->
<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">點(diǎn)擊上傳圖片</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!: {};
// 接收彈窗窗口標(biāo)題
@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)聽(tīng)表單渲染內(nèi)容數(shù)據(jù)接入 + 轉(zhuǎn)換多余傳入問(wè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)聽(tīng)是否彈窗屬性
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表單
使用腳手架新建項(xiàng)目
vue create antd-demo

所以,得到了這么一個(gè)項(xiàng)目,如下

安裝并導(dǎo)入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>啟動(dòng)應(yīng)用,測(cè)試驗(yàn)證
npm run serve啟動(dòng)應(yīng)用,效果如下

好了,應(yīng)用就暫時(shí)介紹到這里。其實(shí),我更想說(shuō)說(shuō)我的疑惑:
Hello.vue中,Username輸入框的前面有個(gè)圖片前綴,Password輸入框的前面也有一個(gè)圖片前綴,都是通過(guò)<template #prefix></template>實(shí)現(xiàn)的,一眼看去,應(yīng)該就是通過(guò)插槽實(shí)現(xiàn)的,但是具體的實(shí)現(xiàn)過(guò)程是怎樣的,尚不清楚。
簡(jiǎn)單調(diào)試了一下,如下圖所示。

ant-design-vue的Form組件的FormItem.js的部分源碼如下,

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
vue3+vite動(dòng)態(tài)加載路由,本地路由和線上路由匹配方式
這篇文章主要介紹了vue3+vite動(dòng)態(tài)加載路由,本地路由和線上路由匹配方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-06-06
vue打開(kāi)新窗口并實(shí)現(xiàn)傳參的圖文實(shí)例
這篇文章主要給大家介紹了關(guān)于vue打開(kāi)新窗口并實(shí)現(xiàn)傳參的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03
Vue Router去掉url中默認(rèn)的錨點(diǎn)#
vue-router默認(rèn)hash模式——使用URL的hash來(lái)模擬一個(gè)完整的URL,于是當(dāng)URL改變時(shí),頁(yè)面不會(huì)重新加載。這篇文章主要介紹了Vue Router去掉url中默認(rèn)的錨點(diǎn)#,需要的朋友可以參考下2018-08-08
vue?目錄樹(shù)的展開(kāi)與關(guān)閉的實(shí)現(xiàn)
Vue作為一款流行的前端框架,提供了一種數(shù)據(jù)驅(qū)動(dòng)的方式來(lái)實(shí)現(xiàn)目錄樹(shù),本文主要介紹了vue?目錄樹(shù)的展開(kāi)與關(guān)閉的實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下2023-11-11
解決Vue.js應(yīng)用回退或刷新界面時(shí)提示用戶(hù)保存修改問(wèn)題
這篇文章主要介紹了Vue.js應(yīng)用回退或刷新界面時(shí)提示用戶(hù)保存修改問(wèn)題,本文通過(guò)兩種手段防止運(yùn)營(yíng)編輯時(shí)丟失數(shù)據(jù),具體內(nèi)容詳情,感興趣的朋友跟隨小編一起看看吧2019-11-11
vue如何實(shí)現(xiàn)路由跳轉(zhuǎn)到外部鏈接界面
這篇文章主要介紹了vue如何實(shí)現(xiàn)路由跳轉(zhuǎn)到外部鏈接界面,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。2022-10-10
vue輸入框使用模糊搜索功能的實(shí)現(xiàn)代碼
這篇文章主要介紹了vue輸入框使用模糊搜索功能,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-05-05
vue?LogicFlow更多配置選項(xiàng)示例詳解
這篇文章主要為大家介紹了vue?LogicFlow更多配置選項(xiàng)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01
vue如何隨心所欲調(diào)整el-dialog中body的樣式
這篇文章主要介紹了vue如何隨心所欲調(diào)整el-dialog中body的樣式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-05-05
vue拖拽組件vuedraggable使用說(shuō)明詳解
這篇文章主要為大家詳細(xì)介紹了vue拖拽組件vuedraggable的使用說(shuō)明,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04

