UNPKG

@femessage/el-form-renderer

Version:
1,231 lines (1,110 loc) 43.1 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@babel/runtime/helpers/toConsumableArray'), require('@babel/runtime/helpers/defineProperty'), require('@babel/runtime/helpers/objectSpread'), require('lodash.set'), require('lodash.isequal'), require('lodash.clonedeep'), require('@babel/runtime/helpers/slicedToArray'), require('lodash.get'), require('lodash.has'), require('lodash.frompairs'), require('lodash.isplainobject'), require('lodash.includes'), require('lodash.topairs'), require('@babel/runtime/helpers/extends'), require('lodash.kebabcase')) : typeof define === 'function' && define.amd ? define(['exports', '@babel/runtime/helpers/toConsumableArray', '@babel/runtime/helpers/defineProperty', '@babel/runtime/helpers/objectSpread', 'lodash.set', 'lodash.isequal', 'lodash.clonedeep', '@babel/runtime/helpers/slicedToArray', 'lodash.get', 'lodash.has', 'lodash.frompairs', 'lodash.isplainobject', 'lodash.includes', 'lodash.topairs', '@babel/runtime/helpers/extends', 'lodash.kebabcase'], factory) : (global = global || self, factory(global.ElFormRenderer = {}, global._toConsumableArray, global._defineProperty, global._objectSpread, global._set, global._isequal, global._clonedeep, global._slicedToArray, global._get, global._has, global._frompairs, global._isplainobject, global._includes, global._topairs, global._extends, global._kebabcase)); }(this, function (exports, _toConsumableArray, _defineProperty, _objectSpread, _set, _isequal, _clonedeep, _slicedToArray, _get, _has, _frompairs, _isplainobject, _includes, _topairs, _extends, _kebabcase) { 'use strict'; _toConsumableArray = _toConsumableArray && _toConsumableArray.hasOwnProperty('default') ? _toConsumableArray['default'] : _toConsumableArray; _defineProperty = _defineProperty && _defineProperty.hasOwnProperty('default') ? _defineProperty['default'] : _defineProperty; _objectSpread = _objectSpread && _objectSpread.hasOwnProperty('default') ? _objectSpread['default'] : _objectSpread; _set = _set && _set.hasOwnProperty('default') ? _set['default'] : _set; _isequal = _isequal && _isequal.hasOwnProperty('default') ? _isequal['default'] : _isequal; _clonedeep = _clonedeep && _clonedeep.hasOwnProperty('default') ? _clonedeep['default'] : _clonedeep; _slicedToArray = _slicedToArray && _slicedToArray.hasOwnProperty('default') ? _slicedToArray['default'] : _slicedToArray; _get = _get && _get.hasOwnProperty('default') ? _get['default'] : _get; _has = _has && _has.hasOwnProperty('default') ? _has['default'] : _has; _frompairs = _frompairs && _frompairs.hasOwnProperty('default') ? _frompairs['default'] : _frompairs; _isplainobject = _isplainobject && _isplainobject.hasOwnProperty('default') ? _isplainobject['default'] : _isplainobject; _includes = _includes && _includes.hasOwnProperty('default') ? _includes['default'] : _includes; _topairs = _topairs && _topairs.hasOwnProperty('default') ? _topairs['default'] : _topairs; _extends = _extends && _extends.hasOwnProperty('default') ? _extends['default'] : _extends; _kebabcase = _kebabcase && _kebabcase.hasOwnProperty('default') ? _kebabcase['default'] : _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; exports.default = Component; Object.defineProperty(exports, '__esModule', { value: true }); }));