@caxa-form/core
Version:
vue动态表单,助你轻松搞定表单|form-create is a form generation component that can generate dynamic rendering, data collection, verification and submission functions through JSON. Supports 3 UI frameworks, and supports the generation of any Vue components. Built-in 20
310 lines (278 loc) • 9.26 kB
JavaScript
import $FormCreate from '../components/formCreate';
import Vue from 'vue';
import makerFactory from '../factory/maker';
import Handle from '../handler';
import fetch from './fetch';
import {creatorFactory} from '..';
import BaseParser from '../factory/parser';
import {copyRule, copyRules, mergeGlobal, parseJson, toJson} from './util';
import fragment from '../components/fragment';
import is from '@caxa-form/utils/lib/type';
import toCase from '@caxa-form/utils/lib/tocase';
import extend from '@caxa-form/utils/lib/extend';
import {CreateNodeFactory} from '../factory/node';
import {createManager} from '../factory/manager';
import {arrayAttrs, keyAttrs, normalAttrs} from './attrs';
import {appendProto} from '../factory/creator';
import $fetch from './provider';
import {deepCopy} from '@caxa-form/utils/lib/deepextend';
export let _vue = typeof window !== 'undefined' && window.Vue ? window.Vue : Vue;
function _parseProp(name, id) {
let prop;
if (arguments.length === 2) {
prop = arguments[1];
id = prop[name];
} else {
prop = arguments[2];
}
return {id, prop};
}
function nameProp() {
return _parseProp('name', ...arguments);
}
function _getEl(options) {
if (!options || !options.el) return window.document.body;
return is.Element(options.el)
? options.el
: document.querySelector(options.el);
}
function mountForm(rules, option) {
const $vm = new _vue({
data() {
//todo 外部无法修改
return {rule: rules, option: option || {}};
},
render(h) {
return h('FormCreate', {ref: 'fc', props: this.$data});
}
});
$vm.$mount();
return $vm;
}
function exportAttrs(attrs) {
const key = attrs.key || [];
const array = attrs.array || [];
const normal = attrs.normal || [];
keyAttrs.push(...key);
arrayAttrs.push(...array);
normalAttrs.push(...normal);
appendProto([...key, ...array, ...normal]);
}
//todo 表单嵌套
export default function FormCreateFactory(config) {
const components = {
[fragment.name]: fragment
};
const parsers = {};
const directives = {};
const providers = {
fetch: $fetch
};
const maker = makerFactory();
let globalConfig = {global: {}};
const data = {};
const CreateNode = CreateNodeFactory();
exportAttrs(config.attrs || {});
function directive() {
const data = nameProp(...arguments);
if (data.id && data.prop) directives[data.id] = data.prop;
}
function register() {
const data = nameProp(...arguments);
if (data.id && data.prop) providers[data.id] = {...data.prop, name: data.id};
}
function componentAlias(alias) {
CreateNode.use(alias);
}
function parser() {
const data = nameProp(...arguments);
if (!data.id || !data.prop) return;
const name = toCase(data.id);
const parser = data.prop;
const base = parser.merge === true ? parsers[name] : undefined;
parsers[name] = {...(base || BaseParser), ...parser};
maker[name] = creatorFactory(name);
parser.maker && extend(maker, parser.maker);
}
function component(id, component) {
let name;
if (is.String(id)) {
name = toCase(id);
if (['form-create', 'formcreate'].indexOf(name) > -1) {
return $form();
} else if (component === undefined) {
return components[name];
}
} else {
name = toCase(id.name);
component = id;
}
if (!name || !component) return;
components[name] = component;
if (component.formCreateParser) parser(name, component.formCreateParser);
}
function $form() {
return _vue.extend($FormCreate(FormCreate));
}
//todo 检查回调函数作用域
function use(fn, opt) {
if (is.Function(fn.install)) fn.install(create, opt);
else if (is.Function(fn)) fn(create, opt);
return this;
}
function create(rules, _opt, parent) {
let $vm = mountForm(rules, _opt || {});
const _this = $vm.$refs.fc.formCreate;
_this.$parent = parent;
_getEl(_this.options).appendChild($vm.$el);
return _this.api();
}
function FormCreate(vm, rules, options) {
extend(this, {
vm,
manager: createManager(config.manager),
parsers,
providers,
rules: Array.isArray(rules) ? rules : [],
prop: {
components,
directives,
},
CreateNode,
bus: new _vue,
unwatch: null,
extendApi: config.extendApi || (api => api)
})
this.init();
this.initOptions(options || {});
}
extend(FormCreate.prototype, {
init() {
const vm = this.vm;
const h = new Handle(this);
this.$handle = h;
vm.$f = h.api;
vm.$emit('input', h.api);
vm.$on('hook:created', () => {
if (this.isSub()) {
this.unwatch = vm.$watch(() => vm.$pfc.option, () => {
this.initOptions(this.options);
vm.$f.refresh();
}, {deep: true});
this.initOptions(this.options);
}
this.created();
})
vm.$on('hook:mounted', () => {
this.mounted();
});
vm.$on('hook:beforeDestroy', () => {
vm.destroyed = true;
this.unwatch && this.unwatch();
h.reloadRule([]);
});
vm.$on('hook:updated', () => {
h.bindNextTick(() => this.bus.$emit('next-tick', h.api));
});
},
isSub() {
return this.vm.$pfc && this.vm.extendOption;
},
initOptions(options) {
this.options = {formData: {}, submitBtn: {}, resetBtn: {}, ...deepCopy(globalConfig)};
if (this.isSub()) {
this.mergeOptions(this.options, this.vm.$pfc.$f.config || {}, true);
}
this.updateOptions(options);
},
mergeOptions(target, opt, parent) {
opt = deepCopy(opt);
parent && ['page', 'onSubmit', 'mounted', 'reload', 'formData', 'el'].forEach((n) => {
delete opt[n];
});
if (opt.global) {
target.global = mergeGlobal(target.global, opt.global);
delete opt.global;
}
this.$handle.$manager.mergeOptions([opt], target);
return target;
},
updateOptions(options) {
this.mergeOptions(this.options, options);
this.$handle.$manager.updateOptions(this.options);
},
created() {
this.$handle.init();
},
api() {
return this.$handle.api;
},
render() {
return this.$handle.render();
},
mounted() {
this.$handle.mounted();
},
})
function useAttr(formCreate) {
extend(formCreate, {
version: config.version,
ui: config.ui,
data,
maker,
component,
directive,
register,
parser,
use,
componentAlias,
copyRule,
copyRules,
fetch,
$form,
parseJson,
toJson,
init(rules, _opt = {}) {
let $vm = mountForm(rules, _opt), _this = $vm.$refs.fc.formCreate;
return {
mount($el) {
if ($el && is.Element($el))
_this.options.el = $el;
_getEl(_this.options).appendChild($vm.$el);
return _this.api();
},
remove() {
$vm.$el.parentNode && $vm.$el.parentNode.removeChild($vm.$el);
},
destroy() {
this.remove();
$vm.$destroy();
},
$f: _this.api()
};
}
});
}
function useStatic(formCreate) {
extend(formCreate, {
create,
install(Vue, options) {
globalConfig = {...globalConfig, ...(options || {})}
if (Vue._installedFormCreate === true) return;
Vue._installedFormCreate = true;
_vue = Vue;
const $formCreate = function (rules, opt = {}) {
return create(rules, opt, this);
};
useAttr($formCreate);
Vue.prototype.$formCreate = $formCreate;
Vue.component('FormCreate', $form());
}
})
}
useAttr(create);
useStatic(create);
CreateNode.use({fragment: 'fcFragment'});
if (config.install) create.use(config);
return create;
}