Commit caa734fae78f0ee2c630668d2d4927287d1f18c8

Authored by liuhanchen
1 parent 8104d35f

feat: 表格支持行内校验

packages/table/editor.vue
@@ -37,49 +37,82 @@ function cellRender(h, context, item) { @@ -37,49 +37,82 @@ function cellRender(h, context, item) {
37 }; 37 };
38 } 38 }
39 39
  40 +// 表单项渲染
  41 +function formItemRender(h, context, item, scope, children) {
  42 + // 渲染函数配置
  43 + const contentProps = context.props || {};
  44 + // 编辑器统一配置
  45 + const editorProps = get(contentProps, 'editor.props') || {};
  46 + let formItemProp = [editorProps.path].filter(i => i);
  47 + formItemProp.push(scope.$index);
  48 + formItemProp.push(item.prop);
  49 + formItemProp = formItemProp.join('.');
  50 + return h(
  51 + editorProps.formItem || 'el-form-item',
  52 + {
  53 + props: { prop: formItemProp, rules: item.rules, 'inline-message': true },
  54 + },
  55 + children,
  56 + );
  57 +}
  58 +
40 // 编辑器渲染 59 // 编辑器渲染
41 function editorRender(h, context, item) { 60 function editorRender(h, context, item) {
42 const editorSlot = context.scopedSlots[`editor-${item.prop}`]; 61 const editorSlot = context.scopedSlots[`editor-${item.prop}`];
43 const contentProps = context.props || {}; 62 const contentProps = context.props || {};
44 return function(scope) { 63 return function(scope) {
45 const value = get(scope.row, item.prop); 64 const value = get(scope.row, item.prop);
46 - // 自定义具名插槽  
47 - if (editorSlot) {  
48 - return editorSlot({ item, value, index: scope.$index, ...scope });  
49 - } 65 + let vnode = {};
50 // 默认 66 // 默认
51 - const vnode = h(item.is, {  
52 - attrs: item.attrs,  
53 - props: { ...(item.props || {}), value },  
54 - on: {  
55 - input(val) {  
56 - if (get(contentProps, 'editor.deep') === true) {  
57 - if (item.prop.indexOf('.') > -1 || item.prop.indexOf('[') > -1) {  
58 - let separator = '';  
59 - if (item.prop.indexOf('.') > -1) {  
60 - separator = '.';  
61 - } else if (item.prop.indexOf('[') > -1) {  
62 - separator = '[';  
63 - }  
64 - const path = item.prop.split(separator);  
65 - const bindProp = path[0];  
66 - const propValue = cloneDeep(scope.row);  
67 - set(propValue, item.prop, val);  
68 - vnode.componentInstance.$set(scope.row, bindProp, propValue[bindProp]);  
69 - } else {  
70 - // set(scope.row, item.prop, val);  
71 - scope.row[item.prop] = val;  
72 - }  
73 - } else {  
74 - scope.row[item.prop] = val;  
75 - // set(contentProps.data, `[${[scope.$index]}]${item.prop}`, val); 67 + const inputEvent = val => {
  68 + if (get(contentProps, 'editor.deep') === true) {
  69 + if (item.prop.indexOf('.') > -1 || item.prop.indexOf('[') > -1) {
  70 + let separator = '';
  71 + if (item.prop.indexOf('.') > -1) {
  72 + separator = '.';
  73 + } else if (item.prop.indexOf('[') > -1) {
  74 + separator = '[';
76 } 75 }
77 - if (item.on && item.on.input) {  
78 - item.on.input(val);  
79 - }  
80 - },  
81 - }, 76 + const path = item.prop.split(separator);
  77 + const bindProp = path[0];
  78 + const propValue = cloneDeep(scope.row);
  79 + set(propValue, item.prop, val);
  80 + vnode.componentInstance.$set(scope.row, bindProp, propValue[bindProp]);
  81 + } else {
  82 + // set(scope.row, item.prop, val);
  83 + scope.row[item.prop] = val;
  84 + }
  85 + } else {
  86 + scope.row[item.prop] = val;
  87 + // set(contentProps.data, `[${[scope.$index]}]${item.prop}`, val);
  88 + }
  89 + if (item.on && item.on.input) {
  90 + item.on.input(val);
  91 + }
  92 + };
  93 + const blurEvent = val => {
  94 + if (item.on && item.on.blur) {
  95 + item.on.blur(val);
  96 + }
  97 + };
  98 + // 编辑表单项配置
  99 + const itemProps = item.props || {};
  100 + // 编辑器统一配置
  101 + const editorProps = get(contentProps, 'editor.props') || {};
  102 + // 生成虚拟节点
  103 + vnode = h(item.is, {
  104 + attrs: item.attrs,
  105 + props: { ...editorProps, ...itemProps, value },
  106 + on: { input: inputEvent, blur: blurEvent },
82 }); 107 });
  108 + // 自定义具名插槽
  109 + if (editorSlot) {
  110 + return editorSlot({ item, value, index: scope.$index, ...scope, onInput: inputEvent });
  111 + }
  112 + // 需要校验时外层嵌套校验组件
  113 + if (editorProps.validate) {
  114 + return formItemRender(h, context, item, scope, [vnode]);
  115 + }
83 return vnode; 116 return vnode;
84 }; 117 };
85 } 118 }
@@ -90,11 +123,18 @@ function createElTableColumns(h, context, columns) { @@ -90,11 +123,18 @@ function createElTableColumns(h, context, columns) {
90 const editorConfig = props.editor || {}; 123 const editorConfig = props.editor || {};
91 return columns.map((item, index) => { 124 return columns.map((item, index) => {
92 const { attrs, on, ...props } = item; 125 const { attrs, on, ...props } = item;
93 - const editorMatch = editorConfig.inputs.find(i => i.prop === item.prop); 126 + const items = editorConfig.items || [];
  127 + // 当前列编辑器配置
  128 + let editorItem = items.find(i => i.prop === item.prop);
  129 + // 当前列有编辑器配置或编辑器插槽的情况
  130 + const isEditor = editorItem || context.scopedSlots[`editor-${item.prop}`];
  131 + if (context.scopedSlots[`editor-${item.prop}`] && !editorItem) {
  132 + editorItem = item;
  133 + }
94 // 处理插槽 134 // 处理插槽
95 const scopedSlots = { 135 const scopedSlots = {
96 header: headerRender(h, context, item), 136 header: headerRender(h, context, item),
97 - default: editorMatch ? editorRender(h, context, editorMatch) : cellRender(h, context, item), 137 + default: isEditor ? editorRender(h, context, editorItem) : cellRender(h, context, item),
98 }; 138 };
99 return h('el-table-column', { key: index, attrs, props, on, scopedSlots }); 139 return h('el-table-column', { key: index, attrs, props, on, scopedSlots });
100 }); 140 });
@@ -106,6 +146,9 @@ export default { @@ -106,6 +146,9 @@ export default {
106 render(h, context) { 146 render(h, context) {
107 console.log(context); 147 console.log(context);
108 const props = context.props || {}; 148 const props = context.props || {};
  149 + // 设置默认class名称,用来追加一些默认的样式
  150 + const className = get(context, 'data.class');
  151 + set(context, 'data.class', className ? `${className} z-table-editor` : 'z-table-editor');
109 let scopedSlots = context.scopedSlots || {}; 152 let scopedSlots = context.scopedSlots || {};
110 // 如有默认插槽则相当于直接写el-table 153 // 如有默认插槽则相当于直接写el-table
111 if (scopedSlots.default) { 154 if (scopedSlots.default) {
@@ -128,3 +171,16 @@ export default { @@ -128,3 +171,16 @@ export default {
128 }, 171 },
129 }; 172 };
130 </script> 173 </script>
  174 +
  175 +<style lang="scss">
  176 +.z-table-editor {
  177 + &.el-table td .cell {
  178 + padding-top: 2px !important;
  179 + padding-bottom: 2px !important;
  180 + padding-right: 2px !important;
  181 + }
  182 + .el-form-item {
  183 + margin-bottom: 0 !important;
  184 + }
  185 +}
  186 +</style>
packages/table/index.vue
@@ -9,7 +9,7 @@ export default { @@ -9,7 +9,7 @@ export default {
9 if (Object.prototype.hasOwnProperty.call(props, 'editable') && props.editable !== false) { 9 if (Object.prototype.hasOwnProperty.call(props, 'editable') && props.editable !== false) {
10 return h('z-table-editable', { props, scopedSlots: context.scopedSlots, on: context.listeners }); 10 return h('z-table-editable', { props, scopedSlots: context.scopedSlots, on: context.listeners });
11 } 11 }
12 - if (props.editor) { 12 + if (Object.prototype.hasOwnProperty.call(props, 'editor')) {
13 return ref('z-table-editor', context); 13 return ref('z-table-editor', context);
14 } 14 }
15 return ref('z-table-normal', context); 15 return ref('z-table-normal', context);