vue中封装弹窗表单的实现是什么样的
Admin 2022-09-05 群英技术资讯 536 次浏览
<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 create antd-demo
所以,得到了这么一个项目,如下
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的部分源码如下,
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:mmqy2019@163.com进行举报,并提供相关证据,查实之后,将立刻删除涉嫌侵权内容。
猜你喜欢
Node如何实现数据传输加密解密?下面本篇文章给大家介绍一下Node.js实现前后端数据传输加密解密的方法,希望对大家有所帮助!
JavaScript是一种前端开发语言,与其他高级语言一样,JavaScript也有new操作符。那么在js中new操作符的作用是什么呢?很多刚接触前端朋友对此都不是很了解,本文详细介绍了js中new操作符的作用,感兴趣的朋友继续往下看吧。
这篇文章主要为大家详细介绍了Vue计时器的用法,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
目录elementUI 如何处理文件批量上传问题解决elementUI批量上传下载注意事项在el-upload组件关键的钩子,其它省略点击上传,将多个文件和表单数据一起上传elementUI 如何处理文件批量上传问题elementUI的Upload上传组件,通过设置multiple为true,就可以实现多选文件;但是在
这些年来,ES6 将js的可用性提升到一个新的水平时: 箭头函数、类等等,这些都很棒。箭头函数是最有价值的新功能之一,有很多好文章描述了它的上下文透明性和简短的语法。
成为群英会员,开启智能安全云计算之旅
立即注册Copyright © QY Network Company Ltd. All Rights Reserved. 2003-2020 群英 版权所有
增值电信经营许可证 : B1.B2-20140078 粤ICP备09006778号 域名注册商资质 粤 D3.1-20240008