@femessage/el-form-renderer
Version:
render form-item easily
1,221 lines (1,103 loc) • 38.4 kB
JavaScript
import _toConsumableArray from '@babel/runtime/helpers/toConsumableArray';
import _defineProperty from '@babel/runtime/helpers/defineProperty';
import _objectSpread from '@babel/runtime/helpers/objectSpread';
import _set from 'lodash.set';
import _isequal from 'lodash.isequal';
import _clonedeep from 'lodash.clonedeep';
import _slicedToArray from '@babel/runtime/helpers/slicedToArray';
import _get from 'lodash.get';
import _has from 'lodash.has';
import _frompairs from 'lodash.frompairs';
import _isplainobject from 'lodash.isplainobject';
import _includes from 'lodash.includes';
import _topairs from 'lodash.topairs';
import _extends from '@babel/runtime/helpers/extends';
import _kebabcase from 'lodash.kebabcase';
/**
* 处理 enableWhen
*
* 与条件: 简单依赖关系存在2种情况:简单对象 || 字符串
* 或条件: 即使用 [] 包裹所有与条件 enableWhen: [{ a: 1 }, { a: 2 }]
*/
function getEnableWhenStatus(enableWhen, value) {
if (!enableWhen) return true; // 处理一个与条件
var handleCondition = function handleCondition(condition) {
// 简单字符串(ID), 只要有值即为true
if (typeof condition === 'string') return _has(value, condition); // 简单对象判断: 是否所有依赖条件都通过
return Object.keys(condition).every(function (path) {
var v = _get(value, path);
return v !== undefined && v === condition[path];
});
};
return Array.isArray(enableWhen) ? enableWhen.some(handleCondition) : handleCondition(enableWhen);
}
function noop() {}
function collect(content, key) {
return _frompairs(content.map(function (item) {
return {
id: item.id,
type: item.type,
value: item.type === 'group' ? collect(item.items, key) : item[key]
};
}).filter(function (_ref) {
var type = _ref.type,
value = _ref.value;
return value !== undefined || type === 'group' && Object.keys(value).length;
}).map(function (_ref2) {
var id = _ref2.id,
value = _ref2.value;
return [id, value];
}));
}
/**
* 递归合并 oldV & newV,策略如下:
* 1. 如果该项的 type 不是 GROUP,直接覆盖合并到 oldV
* 2. 如果是,则递归执行步骤 1
*/
function mergeValue(oldV, newV, content) {
Object.keys(newV).forEach(function (k) {
var item = content.find(function (item) {
return item.id === k;
}) || {};
if (item.type !== 'group') oldV[k] = newV[k];else mergeValue(oldV[k], newV[k], item.items);
});
}
/**
* 根据 content 中的 outputFormat 来处理 value;
* 如果 outputFormat 处理后的值是对象类型,会覆盖(Object.assign)到 value 上
*/
function transformOutputValue(value, content) {
var _ref3 = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {},
_ref3$strict = _ref3.strict,
strict = _ref3$strict === void 0 ? false : _ref3$strict;
var newVal = strict ? {} : _objectSpread({}, value);
Object.keys(value).forEach(function (id) {
var item = content.find(function (item) {
return item.id === id;
});
if (!item) return;
if (item.type !== 'group') {
if (item.outputFormat) {
var v = item.outputFormat(value[id]); // REVIEW: 仅根据 format 后的类型来判断赋值形式,有些隐晦
if (_isplainobject(v)) Object.assign(newVal, v);else newVal[id] = v;
} else {
newVal[id] = value[id];
}
} else {
newVal[id] = transformOutputValue(value[id], item.items, {
strict: strict
});
}
});
return newVal;
}
/**
* 根据 content 中的 inputFormat 来处理 value
* inputFormat 接受的是当前层级的 value
* 复杂点在于,不管传入的 value 是否包含某表单项的 key,所有使用了 inputFormat 的项都有可能在这次 update 中被更新
*/
function transformInputValue(value, content) {
var newVal = _objectSpread({}, value);
content.forEach(function (item) {
var id = item.id;
if (item.inputFormat) {
var v = item.inputFormat(value);
if (v !== undefined) newVal[id] = v;
} else if (id in value) {
if (item.type !== 'group') {
newVal[id] = value[id];
} else {
newVal[id] = transformInputValue(value[id], item.items);
}
}
});
return newVal;
}
function correctValue(value, content) {
content.forEach(function (_ref4) {
var type = _ref4.type,
id = _ref4.id,
items = _ref4.items;
switch (type) {
case 'group':
if (!(id in value)) value[id] = {};
correctValue(value[id], items);
break;
case 'checkbox-group':
if (!(id in value)) value[id] = [];
break;
}
});
}
function validator(data) {
if (!data) {
throw new Error('data must be an Object.');
} else if (!data.id) {
throw new Error('`id` is unvalidated.');
} else if (!data.type && !data.component) {
throw new Error('`type` and `component` cannot both be null.');
}
}
var script = {
components: {
/**
* 🐂🍺只需要有组件选项对象,就可以立刻包装成函数式组件在 template 中使用
* FYI: https://cn.vuejs.org/v2/guide/render-function.html#%E5%87%BD%E6%95%B0%E5%BC%8F%E7%BB%84%E4%BB%B6
*/
CustomComponent: {
functional: true,
render: function render(h, ctx) {
return h(ctx.props.component, ctx.data, ctx.children);
}
},
VNode: {
functional: true,
render: function render(h, ctx) {
return ctx.props.content;
}
}
},
/**
* elForm inject from https://github.com/ElemeFE/element/blob/dev/packages/form/src/form.vue#L19
*/
inject: ['elFormRenderer', 'elForm'],
props: {
data: Object,
prop: {
type: String,
default: function _default() {
return this.data.id;
}
},
itemValue: {},
value: Object,
disabled: Boolean,
readonly: Boolean,
options: Array
},
data: function data() {
return {
loading: false,
propsInner: {},
isBlurTrigger: this.data.rules && this.data.rules.some(function (rule) {
return rule.required && rule.trigger === 'blur';
})
};
},
computed: {
// 解构运算符会处理 undefined 的情况
componentProps: function componentProps(_ref) {
var el = _ref.data.el,
propsInner = _ref.propsInner;
return _objectSpread({}, el, propsInner);
},
hasReadonlyContent: function hasReadonlyContent(_ref2) {
var type = _ref2.data.type;
return _includes(['input', 'select'], type);
},
hiddenStatus: function hiddenStatus(_ref3) {
var _ref3$data$hidden = _ref3.data.hidden,
hidden = _ref3$data$hidden === void 0 ? function () {
return false;
} : _ref3$data$hidden,
data = _ref3.data,
value = _ref3.value;
return hidden(value, data);
},
enableWhenStatus: function enableWhenStatus(_ref4) {
var enableWhen = _ref4.data.enableWhen,
value = _ref4.value;
return getEnableWhenStatus(enableWhen, value);
},
// 是否显示
_show: function _show() {
return !this.hiddenStatus && this.enableWhenStatus;
},
listeners: function listeners() {
var _this = this;
var _this$data = this.data,
id = _this$data.id,
_this$data$atChange = _this$data.atChange,
atChange = _this$data$atChange === void 0 ? noop : _this$data$atChange,
_this$data$on = _this$data.on,
on = _this$data$on === void 0 ? {} : _this$data$on,
_this$data$on2 = _this$data.on;
_this$data$on2 = _this$data$on2 === void 0 ? {} : _this$data$on2;
var _this$data$on2$input = _this$data$on2.input,
originOnInput = _this$data$on2$input === void 0 ? noop : _this$data$on2$input,
_this$data$on2$change = _this$data$on2.change,
originOnChange = _this$data$on2$change === void 0 ? noop : _this$data$on2$change,
_this$data$trim = _this$data.trim,
trim = _this$data$trim === void 0 ? true : _this$data$trim,
updateForm = this.$parent.$parent.updateForm;
return _objectSpread({}, _frompairs(_topairs(on).map(function (_ref5) {
var _ref6 = _slicedToArray(_ref5, 2),
eName = _ref6[0],
handler = _ref6[1];
return [eName, function () {
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
return handler(args, updateForm);
}];
})), {
// 手动更新表单数据
input: function input(value) {
_this.$emit('updateValue', {
id: id,
value: value
}); // 更新表单时调用
atChange(id, value);
for (var _len2 = arguments.length, rest = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
rest[_key2 - 1] = arguments[_key2];
}
originOnInput([value].concat(rest), updateForm); // FIXME: rules 的 trigger 只写了 blur,依然会在 input 的时候触发校验!
_this.triggerValidate(id);
},
change: function change(value) {
if (typeof value === 'string' && trim) value = value.trim();
_this.$emit('updateValue', {
id: id,
value: value
});
for (var _len3 = arguments.length, rest = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {
rest[_key3 - 1] = arguments[_key3];
}
originOnChange([value].concat(rest), updateForm); // FIXME: rules 的 trigger 只写了 blur,依然会在 change 的时候触发校验!
_this.triggerValidate(id);
}
});
},
multipleValue: function multipleValue(_ref7) {
var data = _ref7.data,
itemValue = _ref7.itemValue,
_ref7$options = _ref7.options,
options = _ref7$options === void 0 ? [] : _ref7$options;
var multipleSelectValue = _get(data, 'el.multiple') && Array.isArray(itemValue) ? itemValue : [itemValue];
return multipleSelectValue.map(function (val) {
return (options.find(function (op) {
return op.value === val;
}) || {}).label;
}).join();
}
},
watch: {
data: validator,
/**
* 这里其实用 remote 处理了两件事。有机会是可以拆分的
* 1. 基本用法,配置 url 后即可从远程获取某个 prop 注入到组件
* 2. 针对 select、checkbox-group & radio-group 组件,会直接将 resp 作为 options 处理;label & value 也是直接为这个场景而生的
*/
'data.remote.request': {
handler: function handler(v, oldV) {
// 不应该用 watch data.remote,因为对象引用是同一个 https://cn.vuejs.org/v2/api/#vm-watch (估计当初这样写是为了方便)
// 现改写成:分开处理 remote.request,remote.url
// 至于为什么判断新旧值相同则返回,是因为 form 的 content 是响应式的,防止用户直接修改 content 其他内容时,导致 remote.request 重新发请求
if (!v || typeof v !== 'function' || v === oldV) return;
this.makingRequest(this.data.remote);
},
immediate: true
},
/**
* 设计意图:外部修改 url, 重新发送请求。如果同时存在 url 与 request,则请 request 为准。
*/
'data.remote.url': {
handler: function handler(url, oldV) {
var _this2 = this;
// 第三个判断条件:防止 url 与 request 同时存在时,发送两次请求
if (!url || url === oldV || !oldV && this.data.remote.request) return;
var request = this.data.remote.request || function () {
return _this2.$axios.get(url).then(function (resp) {
return resp.data;
});
};
this.makingRequest(Object.assign({}, this.data.remote, {
request: request
}));
},
immediate: true
}
},
methods: {
triggerValidate: function triggerValidate(id) {
var _this3 = this;
if (!this.data.rules || !this.data.rules.length) return;
if (this.isBlurTrigger) return;
this.$nextTick(function () {
_this3.elForm && _this3.elForm.validateField(id);
});
},
optionKey: function optionKey(opt) {
if (opt.value instanceof Object) {
if (!this.data.el || !this.data.el.valueKey) {
return;
}
return opt.value[this.data.el.valueKey];
} else {
return opt.value;
}
},
makingRequest: function makingRequest(remoteConfig, query) {
var _this4 = this;
var isOptionsCase = ['select', 'checkbox-group', 'radio-group'].indexOf(this.data.type) > -1;
var request = remoteConfig.request,
_remoteConfig$prop = remoteConfig.prop,
prop = _remoteConfig$prop === void 0 ? 'options' : _remoteConfig$prop,
_remoteConfig$dataPat = remoteConfig.dataPath,
dataPath = _remoteConfig$dataPat === void 0 ? '' : _remoteConfig$dataPat,
_remoteConfig$onRespo = remoteConfig.onResponse,
onResponse = _remoteConfig$onRespo === void 0 ? function (resp) {
if (dataPath) resp = _get(resp, dataPath);
if (isOptionsCase) {
return resp.map(function (item) {
return {
label: item[label],
value: item[value]
};
});
} else {
return resp;
}
} : _remoteConfig$onRespo,
_remoteConfig$onError = remoteConfig.onError,
onError = _remoteConfig$onError === void 0 ? function (error) {
console.error(error.message);
_this4.loading = false;
} : _remoteConfig$onError,
_remoteConfig$label = remoteConfig.label,
label = _remoteConfig$label === void 0 ? 'label' : _remoteConfig$label,
_remoteConfig$value = remoteConfig.value,
value = _remoteConfig$value === void 0 ? 'value' : _remoteConfig$value;
this.loading = true;
Promise.resolve(request(query)).then(onResponse, onError).then(function (resp) {
if (isOptionsCase) {
_this4.elFormRenderer && _this4.elFormRenderer.setOptions(_this4.prop, resp);
} else {
_this4.propsInner = _defineProperty({}, prop, resp);
}
_this4.loading = false;
});
},
remoteMethod: function remoteMethod(query) {
if (_get(this.data, 'type') === 'select' && _get(this.data, 'el.filterable') && _get(this.data, 'el.remote')) {
this.makingRequest(this.data.remote, query);
}
}
}
};
function normalizeComponent(template, style, script, scopeId, isFunctionalTemplate, moduleIdentifier
/* server only */
, shadowMode, createInjector, createInjectorSSR, createInjectorShadow) {
if (typeof shadowMode !== 'boolean') {
createInjectorSSR = createInjector;
createInjector = shadowMode;
shadowMode = false;
} // Vue.extend constructor export interop.
var options = typeof script === 'function' ? script.options : script; // render functions
if (template && template.render) {
options.render = template.render;
options.staticRenderFns = template.staticRenderFns;
options._compiled = true; // functional template
if (isFunctionalTemplate) {
options.functional = true;
}
} // scopedId
if (scopeId) {
options._scopeId = scopeId;
}
var hook;
if (moduleIdentifier) {
// server build
hook = function hook(context) {
// 2.3 injection
context = context || // cached call
this.$vnode && this.$vnode.ssrContext || // stateful
this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext; // functional
// 2.2 with runInNewContext: true
if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') {
context = __VUE_SSR_CONTEXT__;
} // inject component styles
if (style) {
style.call(this, createInjectorSSR(context));
} // register component module identifier for async chunk inference
if (context && context._registeredComponents) {
context._registeredComponents.add(moduleIdentifier);
}
}; // used by ssr in case component is cached and beforeCreate
// never gets called
options._ssrRegister = hook;
} else if (style) {
hook = shadowMode ? function () {
style.call(this, createInjectorShadow(this.$root.$options.shadowRoot));
} : function (context) {
style.call(this, createInjector(context));
};
}
if (hook) {
if (options.functional) {
// register for functional component in vue file
var originalRender = options.render;
options.render = function renderWithStyleInjection(h, context) {
hook.call(context);
return originalRender(h, context);
};
} else {
// inject component registration as beforeCreate hook
var existing = options.beforeCreate;
options.beforeCreate = existing ? [].concat(existing, hook) : [hook];
}
}
return script;
}
var normalizeComponent_1 = normalizeComponent;
/* script */
const __vue_script__ = script;
/* template */
var __vue_render__ = function() {
var _vm = this;
var _h = _vm.$createElement;
var _c = _vm._self._c || _h;
return _vm._show
? _c(
"el-form-item",
_vm._b(
{
staticClass: "render-form-item",
attrs: {
prop: _vm.prop,
label: typeof _vm.data.label === "string" ? _vm.data.label : "",
rules:
!_vm.readonly && Array.isArray(_vm.data.rules)
? _vm.data.rules
: undefined
}
},
"el-form-item",
_vm.data.attrs,
false
),
[
typeof _vm.data.label !== "string"
? _c("v-node", {
attrs: { slot: "label", content: _vm.data.label },
slot: "label"
})
: _vm._e(),
_vm._v(" "),
_vm.readonly && _vm.hasReadonlyContent
? [
_vm.data.type === "input"
? _c(
"el-input",
_vm._g(
_vm._b(
{ attrs: { value: _vm.itemValue, readonly: "" } },
"el-input",
_vm.componentProps,
false
),
_vm.listeners
)
)
: _vm.data.type === "select"
? _c(
"div",
[
[
_vm._v(
"\n " +
_vm._s(_vm.multipleValue) +
"\n "
)
]
],
2
)
: _vm._e()
]
: _c(
"custom-component",
_vm._g(
_vm._b(
{
ref: "customComponent",
attrs: {
component:
_vm.data.component ||
"el-" + (_vm.data.type || "input"),
value: _vm.itemValue,
disabled:
_vm.disabled ||
_vm.componentProps.disabled ||
_vm.readonly,
loading: _vm.loading,
"remote-method":
_vm.data.remoteMethod ||
_vm.componentProps.remoteMethod ||
_vm.remoteMethod
}
},
"custom-component",
_vm.componentProps,
false
),
_vm.listeners
),
[
_vm._l(_vm.options, function(opt, index) {
return [
_vm.data.type === "select"
? _c(
"el-option",
_vm._b(
{ key: _vm.optionKey(opt) || index },
"el-option",
opt,
false
)
)
: _vm.data.type === "checkbox-group" &&
_vm.data.style === "button"
? _c(
"el-checkbox-button",
_vm._b(
{
key: opt.value,
attrs: {
label: "value" in opt ? opt.value : opt.label
}
},
"el-checkbox-button",
opt,
false
),
[
_vm._v(
"\n " + _vm._s(opt.label) + "\n "
)
]
)
: _vm.data.type === "checkbox-group" &&
_vm.data.style !== "button"
? _c(
"el-checkbox",
_vm._b(
{
key: opt.value,
attrs: {
label: "value" in opt ? opt.value : opt.label
}
},
"el-checkbox",
opt,
false
),
[
_vm._v(
"\n " + _vm._s(opt.label) + "\n "
)
]
)
: _vm.data.type === "radio-group" &&
_vm.data.style === "button"
? _c(
"el-radio-button",
_vm._b(
{
key: opt.label,
attrs: {
label: "value" in opt ? opt.value : opt.label
}
},
"el-radio-button",
opt,
false
),
[_vm._v(_vm._s(opt.label))]
)
: _vm.data.type === "radio-group" &&
_vm.data.style !== "button"
? _c(
"el-radio",
_vm._b(
{
key: opt.label,
attrs: {
label: "value" in opt ? opt.value : opt.label
}
},
"el-radio",
opt,
false
),
[_vm._v(_vm._s(opt.label))]
)
: _vm._e()
]
})
],
2
)
],
2
)
: _vm._e()
};
var __vue_staticRenderFns__ = [];
__vue_render__._withStripped = true;
/* style */
const __vue_inject_styles__ = undefined;
/* scoped */
const __vue_scope_id__ = undefined;
/* module identifier */
const __vue_module_identifier__ = undefined;
/* functional template */
const __vue_is_functional_template__ = false;
/* style inject */
/* style inject SSR */
var RenderFormItem = normalizeComponent_1(
{ render: __vue_render__, staticRenderFns: __vue_staticRenderFns__ },
__vue_inject_styles__,
__vue_script__,
__vue_scope_id__,
__vue_is_functional_template__,
__vue_module_identifier__,
undefined,
undefined
);
var script$1 = {
components: {
RenderFormItem: RenderFormItem
},
props: {
data: Object,
itemValue: {},
value: Object,
disabled: Boolean,
readonly: Boolean,
options: Object
},
methods: {
updateValue: function updateValue(_ref) {
var id = _ref.id,
value = _ref.value;
this.$emit('updateValue', {
id: this.data.id,
value: _objectSpread({}, this.itemValue, _defineProperty({}, id, value))
});
}
}
};
/* script */
const __vue_script__$1 = script$1;
/* template */
var __vue_render__$1 = function() {
var _vm = this;
var _h = _vm.$createElement;
var _c = _vm._self._c || _h;
return _c(
"div",
[
_vm._l(_vm.data.items, function(item, index) {
return [
_vm._t("id:" + item.id),
_vm._v(" "),
_vm._t("$id:" + item.id),
_vm._v(" "),
_c("render-form-item", {
key: index,
ref: "formItem-" + item.id,
refInFor: true,
attrs: {
prop: _vm.data.id + "." + item.id,
data: item,
value: _vm.value,
"item-value": _vm.itemValue[item.id],
disabled: _vm.disabled,
readonly: _vm.readonly,
options: _vm.options[item.id]
},
on: { updateValue: _vm.updateValue }
})
]
})
],
2
)
};
var __vue_staticRenderFns__$1 = [];
__vue_render__$1._withStripped = true;
/* style */
const __vue_inject_styles__$1 = undefined;
/* scoped */
const __vue_scope_id__$1 = undefined;
/* module identifier */
const __vue_module_identifier__$1 = undefined;
/* functional template */
const __vue_is_functional_template__$1 = false;
/* style inject */
/* style inject SSR */
var RenderFormGroup = normalizeComponent_1(
{ render: __vue_render__$1, staticRenderFns: __vue_staticRenderFns__$1 },
__vue_inject_styles__$1,
__vue_script__$1,
__vue_scope_id__$1,
__vue_is_functional_template__$1,
__vue_module_identifier__$1,
undefined,
undefined
);
/**
* content 的每一项会浅拷贝一层
* 只可以在 item 层新增修改属性,如 item.a = b
* 不可以直接修改值,避免影响原 content,如 item.a.b = c
*/
function transformContent(content) {
return content.map(function (_ref) {
var item = _extends({}, _ref);
if (item.type === 'group') {
item.items = transformContent(item.items);
} else {
removeDollarInKey(item);
setItemId(item);
extractRulesFromComponent(item); // 有些旧写法是 checkboxGroup & radioGroup
item.type = _kebabcase(item.type);
}
return item;
});
} // 兼容旧写法:$id、$name
function removeDollarInKey(item) {
Object.keys(item).filter(function (k) {
return k.startsWith('$') && !(k.slice(1) in item);
}).forEach(function (k) {
return item[k.slice(1)] = item[k], delete item[k];
});
}
function setItemId(item) {
if (item.id) return; // name 是符合表单项直觉的命名; prop 是为了与 element 的 table 的 columns 匹配
item.id = item.name || item.prop;
}
function extractRulesFromComponent(item) {
if (item.overrideRules) return;
var component = item.component; // 使用全局注册的组件暂时无法处理
if (!component || typeof component === 'string') return;
var _component$rules = component.rules,
rules = _component$rules === void 0 ? [] : _component$rules;
item.rules = [].concat(_toConsumableArray(item.rules || []), _toConsumableArray(typeof rules === 'function' ? rules(item) : rules));
}
var GROUP = 'group';
var script$2 = {
name: 'ElFormRenderer',
components: {
RenderFormItem: RenderFormItem,
RenderFormGroup: RenderFormGroup
},
provide: function provide() {
return {
elFormRenderer: this
};
},
/**
* value 已经被内部大量使用,所以换用 form
*/
model: {
prop: 'form',
event: 'input'
},
props: {
content: {
type: Array,
required: true
},
disabled: {
type: [Boolean, Function],
default: false
},
readonly: {
type: Boolean,
default: false
},
/**
* v-model 的值。传入后会优先使用
*/
form: {
type: Object,
default: undefined
}
},
data: function data() {
return {
GROUP: GROUP,
/**
* inputFormat 让整个输入机制复杂了很多。value 有以下输入路径:
* 1. 传入的 form => inputFormat 处理
* 2. updateForm => inputFormat 处理
* 3. 但 content 中的 default 没法经过 inputFormat 处理,因为 inputFormat 要接受整个 value 作为参数
* 4. 组件内部更新 value,不需要走 inputFormat
*/
value: {},
// 表单数据对象
options: {},
initValue: null
};
},
computed: {
// 用于兼容数据操作
innerContent: function innerContent(_ref) {
var content = _ref.content;
return transformContent(content);
}
},
watch: {
form: {
handler: function handler(v) {
if (!v) return;
this.setValueFromModel();
},
immediate: true,
deep: true
},
innerContent: {
handler: function handler(v) {
// 如果 content 没有变动 remote 的部分,这里需要保留之前 remote 注入的 options
this.options = _objectSpread({}, this.options, collect(v, 'options'));
this.setValueFromModel();
},
immediate: true
},
value: {
handler: function handler(v, oldV) {
if (!v || _isequal(v, oldV)) return;
this.$emit('input', transformOutputValue(v, this.innerContent));
}
}
},
mounted: function mounted() {
var _this = this;
/**
* 与 element 相同,在 mounted 阶段存储 initValue
* @see https://github.com/ElemeFE/element/blob/6ec5f8e900ff698cf30e9479d692784af836a108/packages/form/src/form-item.vue#L304
*/
this.initValue = _clonedeep(this.value);
this.$nextTick(function () {
// proxy
Object.keys(_this.$refs.elForm.$options.methods).forEach(function (item) {
if (item in _this) return;
_this[item] = _this.$refs.elForm[item];
});
/**
* 有些组件会 created 阶段更新初始值为合法值,这会触发 validate。目前已知的情况有:
* - el-select 开启 multiple 时,会更新初始值 undefined 为 []
* @hack
*/
_this.clearValidate();
});
},
methods: {
/**
* 重置表单为初始值
*
* @public
*/
resetFields: function resetFields() {
/**
* 之所以不用 el-form 的 resetFields 机制,有以下原因:
* - el-form 的 resetFields 无视 el-form-renderer 的自定义组件
* - el-form 的 resetFields 不会触发 input & change 事件,无法监听
* - bug1: https://github.com/FEMessage/el-data-table/issues/176#issuecomment-587280825
* - bug2:
* 0. 建议先在监听器 watch.value 里 console.log(v.name, oldV.name)
* 1. 打开 basic 示例
* 2. 在 label 为 name 的输入框里输入 1,此时 log:'1' ''
* 3. 点击 reset 按钮,此时 log 两条数据: '1' '1', '' ''
* 4. 因为 _isequal(v, oldV),所以没有触发 v-model 更新
*/
this.value = _clonedeep(this.initValue);
this.$nextTick(this.clearValidate);
},
setValueFromModel: function setValueFromModel() {
if (!this.innerContent.length) return;
/**
* 没使用 v-model 时才从 default 采集数据
* default 值没法考虑 inputFormat
* 参考 value-format.md 的案例。那种情况下,default 该传什么?
*/
var newValue = this.form ? transformInputValue(this.form, this.innerContent) : collect(this.innerContent, 'default');
correctValue(newValue, this.innerContent);
if (!_isequal(this.value, newValue)) this.value = newValue;
},
/**
* 更新表单数据
* @param {String} options.id 表单ID
* @param {All} options.value 表单数据
*/
updateValue: function updateValue(_ref2) {
var id = _ref2.id,
value = _ref2.value;
this.value = _objectSpread({}, this.value, _defineProperty({}, id, value));
},
/**
* 当 strict 为 true 时,只返回设置的表单项的值, 过滤掉冗余字段, 更多请看 update-form 示例
* @param {{strict: Boolean}} 默认 false
* @return {object} key is item's id, value is item's value
* @public
*/
getFormValue: function getFormValue() {
var _ref3 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
_ref3$strict = _ref3.strict,
strict = _ref3$strict === void 0 ? false : _ref3$strict;
return transformOutputValue(this.value, this.innerContent, {
strict: strict
});
},
/**
* update form values
* @param {object} newValue - key is item's id, value is the new value
* @public
*/
updateForm: function updateForm(newValue) {
newValue = transformInputValue(newValue, this.innerContent);
mergeValue(this.value, newValue, this.innerContent);
this.value = _objectSpread({}, this.value);
},
/**
* update select options
* @param {string} id<br>
* @param {array} options
* @public
*/
setOptions: function setOptions(id, options) {
_set(this.options, id, options);
this.options = _objectSpread({}, this.options); // 设置之前不存在的 options 时需要重新设置响应式更新
},
/**
* get custom component
* @param {string} id<br>
* @public
*/
getComponentById: function getComponentById(id) {
var content = [];
this.content.forEach(function (item) {
if (item.type === GROUP) {
var items = item.items.map(function (formItem) {
formItem.groupId = item.id;
return formItem;
});
content.push.apply(content, _toConsumableArray(items));
} else {
content.push(item);
}
});
var itemContent = content.find(function (item) {
return item.id === id;
});
if (!itemContent) {
return undefined;
}
if (itemContent.groupId) {
var componentRef = this.$refs[itemContent.groupId][0];
return componentRef.$refs["formItem-".concat(id)][0].$refs.customComponent;
} else {
var _componentRef = this.$refs[id][0];
return _componentRef.$refs.customComponent;
}
}
}
};
/* script */
const __vue_script__$2 = script$2;
/* template */
var __vue_render__$2 = function() {
var _vm = this;
var _h = _vm.$createElement;
var _c = _vm._self._c || _h;
return _c(
"el-form",
_vm._b(
{
ref: "elForm",
staticClass: "el-form-renderer",
attrs: { model: _vm.value }
},
"el-form",
_vm.$attrs,
false
),
[
_vm._l(_vm.innerContent, function(item) {
return [
_vm._t("id:" + item.id),
_vm._v(" "),
_vm._t("$id:" + item.id),
_vm._v(" "),
_c(
item.type === _vm.GROUP ? "render-form-group" : "render-form-item",
{
key: item.id,
ref: item.id,
refInFor: true,
tag: "component",
attrs: {
data: item,
value: _vm.value,
"item-value": _vm.value[item.id],
disabled:
_vm.disabled ||
(typeof item.disabled === "function"
? item.disabled(_vm.value)
: item.disabled),
readonly: _vm.readonly || item.readonly,
options: _vm.options[item.id]
},
on: { updateValue: _vm.updateValue }
}
)
]
}),
_vm._v(" "),
_vm._t("default")
],
2
)
};
var __vue_staticRenderFns__$2 = [];
__vue_render__$2._withStripped = true;
/* style */
const __vue_inject_styles__$2 = undefined;
/* scoped */
const __vue_scope_id__$2 = undefined;
/* module identifier */
const __vue_module_identifier__$2 = undefined;
/* functional template */
const __vue_is_functional_template__$2 = false;
/* style inject */
/* style inject SSR */
var Component = normalizeComponent_1(
{ render: __vue_render__$2, staticRenderFns: __vue_staticRenderFns__$2 },
__vue_inject_styles__$2,
__vue_script__$2,
__vue_scope_id__$2,
__vue_is_functional_template__$2,
__vue_module_identifier__$2,
undefined,
undefined
);
// Import vue component
// the same plugin more than once,
// so calling it multiple times on the same plugin
// will install the plugin only once
Component.install = function (Vue) {
Vue.component(Component.name, Component);
}; // To auto-install when vue is found
var GlobalVue = null;
if (typeof window !== 'undefined') {
GlobalVue = window.Vue;
} else if (typeof global !== 'undefined') {
GlobalVue = global.Vue;
}
if (GlobalVue) {
GlobalVue.use(Component);
} // To allow use as module (npm/webpack/etc.) export component
// also be used as directives, etc. - eg. import { RollupDemoDirective } from 'rollup-demo';
// export const RollupDemoDirective = component;
export default Component;