198 lines
5.0 KiB
Vue
198 lines
5.0 KiB
Vue
<template>
|
|
<view class="dynamic-form">
|
|
<view v-for="(field, index) in fields" :key="field.id || index" class="form-item"
|
|
:class="{ 'form-item-up': isUpType(field.type) }">
|
|
<view class="form-header">
|
|
<text class="form-label" :class="{ required: field.is_required == 1 }">{{ field.label }}</text>
|
|
<text v-if="field.tips" class="form-tips">{{ field.tips }}</text>
|
|
</view>
|
|
<view class="form-control">
|
|
<!-- 单行文本 -->
|
|
<form-item-text v-if="field.type === 'text'" :value="formData[field.key]"
|
|
:placeholder="field.placeholder" @input="onInput(field.key, $event)" />
|
|
<!-- 多行文本 -->
|
|
<form-item-textarea v-else-if="field.type === 'textarea'" :value="formData[field.key]"
|
|
:placeholder="field.placeholder" @input="onInput(field.key, $event)" />
|
|
<!-- 数字输入 -->
|
|
<form-item-number v-else-if="field.type === 'number'" :value="formData[field.key]"
|
|
:placeholder="field.placeholder" @input="onInput(field.key, $event)" />
|
|
<!-- 多图上传 -->
|
|
<form-item-image v-else-if="field.type === 'image_multi'" :value="formData[field.key]"
|
|
:maxCount="field.maxCount || 9" @input="onInput(field.key, $event)" />
|
|
<!-- 视频上传 -->
|
|
<form-item-video v-else-if="field.type === 'video'" :value="formData[field.key]"
|
|
@input="onInput(field.key, $event)" />
|
|
<!-- 下拉选择 -->
|
|
<form-item-select v-else-if="field.type === 'select'" :value="formData[field.key]"
|
|
:options="field.options" :placeholder="field.placeholder" @input="onInput(field.key, $event)" />
|
|
<!-- 单选 -->
|
|
<form-item-radio v-else-if="field.type === 'single_select'" :value="formData[field.key]"
|
|
:options="field.options" @input="onInput(field.key, $event)" />
|
|
<!-- 多选 -->
|
|
<form-item-checkbox v-else-if="field.type === 'multi_select'" :value="formData[field.key]"
|
|
:options="field.options" @input="onInput(field.key, $event)" />
|
|
<!-- 图文编辑器 -->
|
|
<form-item-richtext v-else-if="field.type === 'richtext_editor'" :value="formData[field.key]"
|
|
@input="onInput(field.key, $event)" />
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<script>
|
|
import FormItemText from './FormItemText.vue';
|
|
import FormItemTextarea from './FormItemTextarea.vue';
|
|
import FormItemNumber from './FormItemNumber.vue';
|
|
import FormItemImage from './FormItemImage.vue';
|
|
import FormItemVideo from './FormItemVideo.vue';
|
|
import FormItemSelect from './FormItemSelect.vue';
|
|
import FormItemRadio from './FormItemRadio.vue';
|
|
import FormItemCheckbox from './FormItemCheckbox.vue';
|
|
import FormItemRichtext from './FormItemRichtext.vue';
|
|
|
|
export default {
|
|
name: 'DynamicForm',
|
|
components: {
|
|
FormItemText,
|
|
FormItemTextarea,
|
|
FormItemNumber,
|
|
FormItemImage,
|
|
FormItemVideo,
|
|
FormItemSelect,
|
|
FormItemRadio,
|
|
FormItemCheckbox,
|
|
FormItemRichtext
|
|
},
|
|
props: {
|
|
fields: {
|
|
type: Array,
|
|
default: function () {
|
|
return [];
|
|
}
|
|
},
|
|
value: {
|
|
type: Object,
|
|
default: function () {
|
|
return {};
|
|
}
|
|
}
|
|
},
|
|
data: function () {
|
|
return {
|
|
formData: {}
|
|
};
|
|
},
|
|
watch: {
|
|
value: {
|
|
handler: function (val) {
|
|
this.formData = Object.assign({}, val);
|
|
},
|
|
immediate: true,
|
|
deep: true
|
|
},
|
|
fields: {
|
|
handler: function (val) {
|
|
var self = this;
|
|
if (val && val.length > 0) {
|
|
val.forEach(function (field) {
|
|
if (self.formData[field.key] === undefined) {
|
|
self.$set(self.formData, field.key, self.getDefaultValue(field.type));
|
|
}
|
|
});
|
|
}
|
|
},
|
|
immediate: true
|
|
}
|
|
},
|
|
methods: {
|
|
isUpType: function (type) {
|
|
return type === 'image_multi' || type === 'video';
|
|
},
|
|
getDefaultValue: function (type) {
|
|
if (type === 'multi_select') {
|
|
return [];
|
|
}
|
|
if (type === 'image_multi') {
|
|
return [];
|
|
}
|
|
return '';
|
|
},
|
|
onInput: function (key, val) {
|
|
this.$set(this.formData, key, val);
|
|
this.$emit('input', Object.assign({}, this.formData));
|
|
},
|
|
validate: function () {
|
|
for (var i = 0; i < this.fields.length; i++) {
|
|
var field = this.fields[i];
|
|
if (field.is_required == 1) {
|
|
var val = this.formData[field.key];
|
|
if (val === undefined || val === null || val === '' || (Array.isArray(val) && val.length === 0)) {
|
|
uni.showToast({
|
|
title: '请填写' + field.label,
|
|
icon: 'none'
|
|
});
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
},
|
|
getData: function () {
|
|
return Object.assign({}, this.formData);
|
|
}
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<style scoped>
|
|
.dynamic-form {
|
|
width: 100%;
|
|
}
|
|
|
|
.form-item {
|
|
background-color: #fff;
|
|
padding: 24rpx;
|
|
display: flex;
|
|
flex-flow: row nowrap;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
box-sizing: border-box;
|
|
border-radius: 10rpx;
|
|
margin-bottom: 24rpx;
|
|
}
|
|
|
|
.form-item-up {
|
|
flex-flow: column nowrap;
|
|
justify-content: space-between;
|
|
align-items: flex-start;
|
|
}
|
|
|
|
.form-header {
|
|
display: flex;
|
|
align-items: center;
|
|
margin-bottom: 16rpx;
|
|
}
|
|
|
|
.form-label {
|
|
font-size: 28rpx;
|
|
color: #333333;
|
|
}
|
|
|
|
.form-label.required::before {
|
|
content: '*';
|
|
color: #e8101e;
|
|
margin-right: 4rpx;
|
|
}
|
|
|
|
.form-tips {
|
|
font-size: 24rpx;
|
|
color: #999999;
|
|
margin-left: 10rpx;
|
|
}
|
|
|
|
.form-control {
|
|
width: 100%;
|
|
flex: 1;
|
|
}
|
|
</style>
|