@form-create/vant
Version:
VantUI版本低代码表单|FormCreate 是一个可以通过 JSON 生成具有动态渲染、数据收集、验证和提交功能的低代码表单生成组件。支持6个UI框架,适配移动端,并且支持生成任何 Vue 组件。内置20种常用表单组件和自定义组件,再复杂的表单都可以轻松搞定。
309 lines (304 loc) • 11 kB
JSX
import {hasProperty} from '@form-create/utils/lib/type';
import {defineComponent, markRaw, nextTick, watch} from 'vue';
import deepExtend, {deepCopy} from '@form-create/utils/lib/deepextend';
import extend from '@form-create/utils/lib/extend';
const NAME = 'fcGroup';
export default defineComponent({
name: NAME,
props: {
field: String,
rule: Array,
expand: Number,
options: Object,
button: {
type: Boolean,
default: true
},
max: {
type: Number,
default: 0
},
min: {
type: Number,
default: 0
},
modelValue: {
type: Array,
default: () => []
},
defaultValue: Object,
sortBtn: {
type: Boolean,
default: false
},
disabled: {
type: Boolean,
default: undefined
},
onBeforeRemove: {
type: Function,
default: () => {
}
},
onBeforeAdd: {
type: Function,
default: () => {
}
},
formCreateInject: Object,
parse: Function,
},
data() {
return {
len: 0,
cacheRule: {},
cacheValue: {},
sort: [],
form: markRaw(this.formCreateInject.form.$form())
}
},
emits: ['update:modelValue', 'change', 'itemMounted', 'remove', 'add'],
watch: {
rule: {
handler(n, o) {
Object.keys(this.cacheRule).forEach(v => {
const item = this.cacheRule[v];
if (item.$f) {
const val = item.$f.formData();
if (n === o) {
item.$f.deferSyncValue(() => {
deepExtend(item.rule, n);
item.$f.setValue(val);
}, true);
} else {
const val = item.$f.formData();
item.$f.once('reloading', () => {
item.$f.setValue(val);
})
item.rule = deepCopy(n);
}
}
})
},
deep: true
},
expand(n) {
let d = n - this.modelValue.length;
if (d > 0) {
this.expandRule(d);
}
},
modelValue: {
handler(n) {
n = n || [];
let keys = this.sort, total = keys.length, len = total - n.length;
if (len < 0) {
for (let i = len; i < 0; i++) {
this.addRule(n.length + i, true);
}
for (let i = 0; i < total; i++) {
this.setValue(keys[i], n[i]);
}
} else {
if (len > 0) {
for (let i = 0; i < len; i++) {
this.removeRule(keys[total - i - 1]);
}
}
n.forEach((val, i) => {
this.setValue(keys[i], n[i]);
});
}
},
deep: true,
}
},
methods: {
_value(v) {
return (v && hasProperty(v, this.field)) ? v[this.field] : v;
},
cache(k, val) {
this.cacheValue[k] = JSON.stringify(val);
},
input(value) {
this.$emit('update:modelValue', value);
this.$emit('change', value);
},
formData(key, formData) {
const cacheRule = this.cacheRule;
const keys = this.sort;
if (keys.filter(k => cacheRule[k] && cacheRule[k].$f).length !== keys.length) {
return;
}
const value = keys.map(k => {
const data = key === k ? formData : {...this.cacheRule[k].$f.form};
const value = this.field ? data[this.field] || null : data;
this.cache(k, value);
return value;
});
this.input(value);
},
setValue(key, value) {
const field = this.field
if (field) {
value = {[field]: this._value(value)};
}
if (this.cacheValue[key] === JSON.stringify(field ? value[field] : value)) {
return;
}
this.cacheRule[key].$f && this.cacheRule[key].$f.coverValue(value);
this.cache(key, value);
},
addRule(i, emit) {
const rule = this.formCreateInject.form.copyRules(this.rule || []);
const options = this.options ? {...this.options} : {
submitBtn: false,
resetBtn: false,
};
if (this.defaultValue) {
if (!options.formData) options.formData = {};
const defVal = deepCopy(this.defaultValue);
extend(options.formData, this.field ? {[this.field]: defVal} : defVal);
}
this.parse && this.parse({rule, options, index: this.sort.length});
this.cacheRule[++this.len] = {rule, options};
this.sort = Object.keys(this.cacheRule);
if (emit) {
nextTick(() => this.$emit('add', rule, Object.keys(this.cacheRule).length - 1));
}
},
add$f(i, key, $f) {
this.cacheRule[key].$f = $f;
nextTick(() => {
this.$emit('itemMounted', $f, Object.keys(this.cacheRule).indexOf(key));
});
},
removeRule(key, emit) {
const index = Object.keys(this.cacheRule).indexOf(key);
delete this.cacheRule[key];
delete this.cacheValue[key];
this.sort = Object.keys(this.cacheRule);
if (emit) {
nextTick(() => this.$emit('remove', index));
}
},
add(i) {
if (this.disabled || false === this.onBeforeAdd(this.modelValue)) {
return;
}
const value = [...this.modelValue];
value.push(this.defaultValue ? deepCopy(this.defaultValue) : (this.field ? null : {}));
this.input(value);
},
del(index, key) {
if (this.disabled || false === this.onBeforeRemove(this.modelValue, index)) {
return;
}
this.removeRule(key, true);
const value = [...this.modelValue];
value.splice(index, 1);
this.input(value);
},
addIcon(key) {
return <div class="_fc-m-group-btn _fc-m-group-plus-minus" onClick={this.add}></div>;
},
delIcon(index, key) {
return <div class="_fc-m-group-btn _fc-m-group-plus-minus _fc-m-group-minus"
onClick={() => this.del(index, key)}></div>
},
sortUpIcon(index) {
return <div class="_fc-m-group-btn _fc-m-group-arrow _fc-m-group-up"
onClick={() => this.changeSort(index, -1)}></div>
},
sortDownIcon(index) {
return <div class="_fc-m-group-btn _fc-m-group-arrow _fc-m-group-down"
onClick={() => this.changeSort(index, 1)}></div>
},
changeSort(index, sort) {
const a = this.sort[index];
this.sort[index] = this.sort[index + sort];
this.sort[index + sort] = a;
this.formCreateInject.subForm(this.sort.map(k=>{
return this.cacheRule[k].$f;
}));
this.formData(0);
},
makeIcon(total, index, key) {
if (this.$slots.button) {
return this.$slots.button({
total,
index,
vm: this,
key,
del: () => this.del(index, key),
add: this.add
});
}
const btn = [];
if ((!this.max || total < this.max) && total === index + 1) {
btn.push(this.addIcon(key));
}
if (total > this.min) {
btn.push(this.delIcon(index, key));
}
if (this.sortBtn && index) {
btn.push(this.sortUpIcon(index));
}
if (this.sortBtn && index !== total - 1) {
btn.push(this.sortDownIcon(index));
}
return btn;
},
emitEvent(name, args, index, key) {
this.$emit(name, ...args, this.cacheRule[key].$f, index);
},
expandRule(n) {
for (let i = 0; i < n; i++) {
this.addRule(i);
}
}
},
created() {
const d = (this.expand || 0) - this.modelValue.length;
for (let i = 0; i < this.modelValue.length; i++) {
this.addRule(i);
}
if (d > 0) {
this.expandRule(d);
}
},
render() {
const keys = this.sort;
const button = this.button;
const Type = this.form;
const disabled = this.disabled;
const children = keys.length === 0 ?
(this.$slots.default ? (this.$slots.default({
vm: this,
add: this.add
})) : <div key={'a_def'} class="_fc-m-group-plus-minus _fc-m-group-add fc-clock"
onClick={this.add}/>) : keys.map((key, index) => {
const {rule, options} = this.cacheRule[key];
const btn = button && !disabled ? this.makeIcon(keys.length, index, key) : [];
return <div class="_fc-m-group-container" key={key}>
<Type
key={key}
{...{
disabled,
'onUpdate:modelValue': (formData) => this.formData(key, formData),
'onEmit-event': (name, ...args) => this.emitEvent(name, args, index, key),
'onUpdate:api': ($f) => this.add$f(index, key, $f),
inFor: true,
modelValue: this.field ? {[this.field]: this._value(this.modelValue[index])} : this.modelValue[index],
rule,
option: options,
extendOption: true
}}
/>
<div class="_fc-m-group-idx">{index + 1}</div>
{(btn.length) ? <div class="_fc-m-group-handle fc-clock">{btn}</div> : null}
</div>
});
return <div key={'con'} class={'_fc-m-group ' + (disabled ? '_fc-m-group-disabled' : '')}>{children}</div>
}
});