UNPKG

@form-create/vant

Version:

Vant 版本(移动端)低代码表单 | FormCreate 是一个可以通过 JSON 生成具有动态渲染、数据收集、验证和提交功能的低代码表单生成组件。

387 lines (378 loc) 14.2 kB
import is, {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'; import {toPromise} from '@form-create/utils'; 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 }, title: { type: [String, Function], default: null }, type: { type: String, default: 'default' }, 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 ? (is.Undef(data[this.field]) ? null : data[this.field]) : 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) { return; } const del = () => { this.removeRule(key, true); const value = [...this.modelValue]; value.splice(index, 1); this.input(value); } toPromise(this.onBeforeRemove(this.modelValue, index)).then(res => { if (false !== res) { del(); } }) }, 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); }, sortIcon(index, total) { const canMoveUp = index > 0; const canMoveDown = index < total - 1; const btn = []; if (!canMoveUp && !canMoveDown) { return btn; } if (this.type === 'card' && canMoveUp && canMoveDown) { // 显示合并的上下箭头按钮 btn.push(<div class="_fc-group-btn _fc-group-sort"> <div class=" _fc-group-sort-up" onClick={() => this.changeSort(index, -1)}></div> <div class=" _fc-group-sort-down" onClick={() => this.changeSort(index, 1)}></div> </div>); return btn; } if (canMoveUp) { btn.push(this.sortUpIcon(index)); } if (canMoveDown) { btn.push(this.sortDownIcon(index)); } return btn; }, 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) { const sortBtn = this.sortIcon(index, total); if (sortBtn) { btn.push(...sortBtn); } } return btn; }, expandRule(n) { for (let i = 0; i < n; i++) { this.modelValue.push(this.field ? null : {}); } this.input([...this.modelValue]); }, getTitle(index, key) { if (typeof this.title === 'function') { return this.title(index, this.modelValue[index], key); } if (typeof this.title === 'string') { return this.title.replace('{index}', index + 1); } return false; } }, 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 isCardType = this.type === 'card'; 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) : []; const title = this.getTitle(index, key); if (isCardType) { return <div class="_fc-m-group-container" key={key}> <div class="_fc-m-group-header"> {title === false ? <div class="_fc-m-group-idx">{index + 1}</div> : null} {title !== false ? <div class="_fc-m-group-title">{title}</div> : null} <div class="_fc-m-group-handle fc-clock"> {(btn.length) ? btn : null} </div> </div> <div class="_fc-m-group-content"> <Type key={key} {...{ disabled, 'onUpdate:modelValue': (formData) => this.formData(key, formData), '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> </div> } else { return <div class="_fc-m-group-container" key={key}> <Type key={key} {...{ disabled, 'onUpdate:modelValue': (formData) => this.formData(key, formData), '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' : '') + (isCardType ? ' _fc-m-group-card' : '')}>{children}</div> } });