Commit db24c0878f51027164b5ba85449135b7b22384a5

Authored by 刘汉宸
1 parent bc4f9b65

feat: 修改SchemaTable组件

examples/views/docs/component/schema-filter.md
1   -# Schema Filter 筛选
  1 +# Schema Filter 方案筛选
2 2  
3 3 通过配置JSON Schema的方式快速生成一个筛选表单
4 4  
... ...
examples/views/docs/component/schema-table.md
1   -# Schema Table 表格
  1 +# Schema Table 方案表格
2 2  
3   -根据JSON Schema配置自动生成表格
  3 +通过配置JSON Schema的方式快速生成一个表格
4 4  
5 5 ## 基础用法
6 6  
7   -配置`list`属性设置JSON Schema配置列表
  7 +`schema`设置配置项,其中**props**参数与`z-table`组件参数相同,**items**则对应`z-table`组件的`columns`。
8 8  
9   -::: snippet `tableProps`设置表格参数
  9 +::: snippet 本质上是通过`schema`的方式实现生成一个`z-table`
10 10  
11 11 ```html
12 12 <template>
13   - <z-schema-table v-model="tableData" :list="list" :tableProps="{ border: true }"></z-schema-table>
  13 + <z-schema-table v-model="model" :schema="schema"></z-schema-table>
14 14 </template>
15 15  
16 16 <script>
17 17 export default {
18 18 data() {
19 19 return {
20   - tableData: [
  20 + model: [
21 21 { name: '张三', age: 16 },
22 22 { name: '李四', age: 24 }
23 23 ],
24   - list: [
25   - { label: '姓名', key: 'name' },
26   - { label: '年龄', key: 'age', props: { 'controls-position': 'right' } },
27   - ]
  24 + schema: {
  25 + items: [
  26 + { label: '姓名', prop: 'name' },
  27 + { label: '年龄', prop: 'age' },
  28 + ]
  29 + }
28 30 }
29 31 }
30 32 }
... ... @@ -33,17 +35,20 @@ export default {
33 35  
34 36 :::
35 37  
36   -## 自定义渲染
37   -
38   -配置`list`属性设置JSON Schema配置列表
  38 +## 自定义列
39 39  
40   -<div class="code-snippet-box">
  40 +支持自定义列的内容
41 41  
42   -::: snippet `render`函数式渲染
  42 +::: snippet 插槽`header-列字段名`可自定义表头的内容,插槽`cell-列字段名`可自定义列单元格的内容,用法与`z-table`相同。
43 43  
44 44 ```html
45 45 <template>
46   - <z-schema-table v-model="tableData" :list="list"></z-schema-table>
  46 + <z-schema-table v-model="tableData" :schema="schema">
  47 + <template #header-age>年龄 <i class="el-icon-question"></i></template>
  48 + <template #cell-gender="{ value }">
  49 + <el-tag size="mini">{{ value }}</el-tag>
  50 + </template>
  51 + </z-schema-table>
47 52 </template>
48 53  
49 54 <script>
... ... @@ -51,30 +56,35 @@ export default {
51 56 data() {
52 57 return {
53 58 tableData: [
54   - { name: '张三', age: 16 },
55   - { name: '李四', age: 24 }
  59 + { name: '张三', age: '31', gender: '男' },
  60 + { name: '李四', age: '27', gender: '女' },
  61 + { name: '王五', age: '16', gender: '男' },
56 62 ],
57   - list: [
58   - { label: '姓名', key: 'name', render: (h, { value }) => { return h('span', { style: { background: 'rgba(255, 0, 0, 0.5)', color: '#fff' } }, value) } },
59   - { label: '年龄', key: 'age', props: { 'controls-position': 'right' } },
60   - ]
61   - }
62   - }
  63 + schema: {
  64 + props: { border: true, size: 'mini' },
  65 + items: [
  66 + { prop: 'name', label: '姓名' },
  67 + { prop: 'age', label: '年龄' },
  68 + { prop: 'gender', label: '性别' },
  69 + ]
  70 + }
  71 + };
  72 + },
63 73 }
64 74 </script>
65 75 ```
66 76  
67 77 :::
68 78  
69   -::: snippet `slot`插槽式渲染
  79 +## 列渲染
  80 +
  81 +除了使用插槽自定义列的内容之外,也支持直接在配置项中写渲染函数
  82 +
  83 +::: snippet 配置项中的`render`可以设置对应列单元格的渲染
70 84  
71 85 ```html
72 86 <template>
73   - <z-schema-table v-model="tableData" :list="list">
74   - <template #value-age="{ value }">
75   - <el-tag v-if="value" :type="value > 16 ? 'success' : 'danger'" size="mini" disable-transitions>{{ value }}</el-tag>
76   - </template>
77   - </z-schema-table>
  87 + <z-schema-table v-model="tableData" :schema="schema"></z-schema-table>
78 88 </template>
79 89  
80 90 <script>
... ... @@ -82,32 +92,50 @@ export default {
82 92 data() {
83 93 return {
84 94 tableData: [
85   - { name: '张三', age: 16 },
86   - { name: '李四', age: 24 }
  95 + { name: '张三', age: '31', gender: '男', ticket: true },
  96 + { name: '李四', age: '27', gender: '女', ticket: false },
  97 + { name: '王五', age: '16', gender: '男', ticket: false },
87 98 ],
88   - list: [
89   - { type: 'el-input', label: '姓名', key: 'name' },
90   - { type: 'el-input-number', label: '年龄', key: 'age', props: { 'controls-position': 'right' } },
91   - ]
92   - }
93   - }
  99 + schema: {
  100 + props: { border: true, size: 'mini' },
  101 + items: [
  102 + { prop: 'name', label: '姓名' },
  103 + { prop: 'age', label: '年龄', render: (value, row, h) => h(Number(value) > 20 ? 'b' : 'span', { style: { color: Number(value) > 20 ? 'red' : 'green' } }, value) },
  104 + { prop: 'gender', label: '性别', render: (value, row, h) => h('el-tag', { props: { size: 'mini', type: 'info' } }, value) },
  105 + { prop: 'ticket', label: '开票', render: value => value ? '是' : '否' },
  106 + ]
  107 + }
  108 + };
  109 + },
94 110 }
95 111 </script>
96 112 ```
97 113  
98 114 :::
99 115  
100   -</div>
  116 +## 追加列
101 117  
102   -## 层级分组
  118 +使用配置项时,**新增的列**则默认追加在**配置项列**之后,使用`left`插槽可在表格的最左侧插入列,顺序在**配置项列**之前
103 119  
104   -支持`group`分组,兼容form组件,使同一个schema能够同时复用在`table`、`form`上
105   -
106   -::: snippet `group`设置分组,其中`key`设置分组对象名称
  120 +::: snippet 用法与`z-table`相同
107 121  
108 122 ```html
109 123 <template>
110   - <z-schema-table v-model="tableData" :list="list" :tableProps="{ border: true }"></z-schema-table>
  124 + <z-schema-table v-model="tableData" :schema="schema">
  125 + <template #left>
  126 + <el-table-column type="selection" width="40"></el-table-column>
  127 + <el-table-column label="左侧的列">
  128 + <template #default="{ $index }">
  129 + 内容 {{ $index }}
  130 + </template>
  131 + </el-table-column>
  132 + </template>
  133 + <el-table-column label="操作" width="120">
  134 + <template #default="{ $index }">
  135 + <el-button type="text">编辑第{{ $index + 1 }}行</el-button>
  136 + </template>
  137 + </el-table-column>
  138 + </z-schema-table>
111 139 </template>
112 140  
113 141 <script>
... ... @@ -115,22 +143,20 @@ export default {
115 143 data() {
116 144 return {
117 145 tableData: [
118   - { name: '张三', age: 16, location: { city: '上海', address: '青浦区' } },
119   - { name: '李四', age: 24, location: { city: '北京', address: '朝阳区' } }
  146 + { name: '张三', age: '31', gender: '男' },
  147 + { name: '李四', age: '27', gender: '女' },
  148 + { name: '王五', age: '16', gender: '男' },
120 149 ],
121   - list: [
122   - { label: '姓名', key: 'name' },
123   - { label: '年龄', key: 'age', props: { 'controls-position': 'right' } },
124   - {
125   - group: { key: 'location' },
126   - list: [
127   - { label: '城市', key: 'city' },
128   - ]
129   - },
130   - { label: '地址', key: 'location.address' },
131   - ]
132   - }
133   - }
  150 + schema: {
  151 + props: { size: 'mini', border: true } ,
  152 + items: [
  153 + { prop: 'name', label: '姓名' },
  154 + { prop: 'age', label: '年龄' },
  155 + { prop: 'gender', label: '性别' },
  156 + ]
  157 + }
  158 + };
  159 + },
134 160 }
135 161 </script>
136 162 ```
... ... @@ -144,10 +170,7 @@ export default {
144 170 参数|说明|类型|可选值|默认值
145 171 -|-|-|-|-
146 172 value | 表格数据 | Array | - | -
147   -list | JSON Schema配置项列表 | Array | - | []
148   -tableProps | 表格参数 | Object | - | -
149   -tableEvents | 表格参数 | Object | - | -
150   -minWidth | 列宽 | Number | - | -
  173 +schema | JSON Schema配置项列表 | Array | - | []
151 174  
152 175 ## Events 事件
153 176  
... ...
examples/views/docs/component/table.md
... ... @@ -132,15 +132,19 @@ export default {
132 132 <div>
133 133 <z-form span="6">
134 134 <z-form-item>编辑模式:<el-switch v-model="editable"></el-switch></z-form-item>
  135 + <z-form-item>编辑全部:<el-switch v-model="editall"></el-switch></z-form-item>
135 136 <z-form-item>双击编辑:<el-switch v-model="clickable"></el-switch></z-form-item>
136 137 </z-form>
137   - <z-table v-model="tableData" :editable="editable" :columns="columns" border :clickable="clickable" @cell-edit-confirm="onCellEditConfirm">
  138 + <z-table v-model="tableData" size="mini" :editable="editable" :columns="columns" border :clickable="clickable" @cell-edit-confirm="onCellEditConfirm" :editall="editall">
138 139 <template #editor-gender="{ value, onInput, index }">
139 140 <el-radio-group size="mini" :value="value" @input="onInput">
140 141 <el-radio-button label="男">男</el-radio-button>
141 142 <el-radio-button label="女">女</el-radio-button>
142 143 </el-radio-group>
143 144 </template>
  145 + <template #cell-gender="{ value }">
  146 + <el-tag size="mini" type="info" disable-transitions>{{ value }}</el-tag>
  147 + </template>
144 148 <el-table-column label="操作" width="80">
145 149 <template #default="{ $index }">
146 150 <el-button type="text" @click="deleteRow($index)">删除</el-button>
... ... @@ -154,6 +158,7 @@ export default {
154 158 export default {
155 159 data() {
156 160 return {
  161 + editall: false,
157 162 editable: true,
158 163 clickable: true,
159 164 tableData: [
... ...
packages/schema-table/cell-editable.vue
... ... @@ -1,90 +0,0 @@
1   -<style>
2   -.z-table-cell-editable {
3   - display: flex;
4   - align-items: center;
5   -}
6   -.z-table-cell-editable .z-table-cell-editable__icon {
7   - cursor: pointer;
8   - vertical-align: middle;
9   - padding-left: 5px;
10   - fill: #2f54eb;
11   -}
12   -</style>
13   -
14   -<template>
15   - <div>
16   - <!-- 可编辑状态 -->
17   - <div v-if="editable" class="z-table-cell-editable">
18   - <component :value="$_get(row, item.fullKey)" :is="item.type" v-bind="item.props" :style="item.style" size="mini" @input="onInput"></component>
19   - <span v-if="btnVisible !== false" @click="onConfirm">
20   - <svg class="z-table-cell-editable__icon" viewBox="0 0 1024 1024" width="24" height="24">
21   - <path d="M235.946667 472.938667l-45.226667 45.312 210.090667 209.514666 432.362666-427.690666-45.013333-45.482667-387.157333 382.976z"></path>
22   - </svg>
23   - </span>
24   - </div>
25   - <!-- 渲染状态 -->
26   - <template v-else>
27   - <!-- 渲染插槽 -->
28   - <template v-if="$scopedSlots['default']">
29   - <slot></slot>
30   - </template>
31   - <!-- 默认渲染 -->
32   - <template v-else>
33   - {{ $_get(row, item.agentKey || item.fullKey) }}
34   - </template>
35   - </template>
36   - </div>
37   -</template>
38   -
39   -<script>
40   -import { get } from '../utils';
41   -
42   -export default {
43   - name: 'cellEditable',
44   - props: {
45   - row: Object,
46   - item: Object,
47   - editable: Boolean,
48   - btnVisible: Boolean,
49   - },
50   - data() {
51   - return {
52   - oldValue: undefined,
53   - value: undefined,
54   - confirm: false,
55   - };
56   - },
57   - watch: {
58   - editable(val) {
59   - if (val) {
60   - this.confirm = false;
61   - this.oldValue = get(this.row, this.item.agentKey || this.item.fullKey);
62   - this.value = get(this.row, this.item.agentKey || this.item.fullKey);
63   - } else {
64   - if (!this.confirm) {
65   - this.$emit('cancel', this.emitData);
66   - }
67   - }
68   - },
69   - },
70   - computed: {
71   - emitData() {
72   - const { oldValue, value, row, item } = this;
73   - return { oldValue, value, row, key: item.key, fullKey: item.fullKey };
74   - },
75   - },
76   - methods: {
77   - $_get: get,
78   - // 组件触发input事件
79   - onInput(value) {
80   - this.value = value;
81   - this.$emit('update', this.emitData);
82   - },
83   - // 当点击确认
84   - onConfirm() {
85   - this.confirm = true;
86   - this.$emit('done', this.emitData);
87   - },
88   - },
89   -};
90   -</script>
packages/schema-table/cell-value-render.js
... ... @@ -1,20 +0,0 @@
1   -import { get } from '../utils';
2   -
3   -export default {
4   - props: { row: Object, column: Object, index: [Number, String], item: Object },
5   - render(h) {
6   - const { row, column, index, item } = this;
7   - if (typeof item.render === 'function') {
8   - return item.render(h, { row, value: get(row, item.fullKey), $index: index, column });
9   - } else {
10   - if (item.render.children instanceof Function) {
11   - return h(
12   - item.render.type,
13   - { props: item.render.props, attrs: item.render.props, style: item.render.style },
14   - item.render.children({ row, value: get(row, item.fullKey), $index: index, column }),
15   - );
16   - }
17   - return h(item.render.type, { props: item.render.props, attrs: item.render.props, style: item.render.style }, item.render.children || get(row, item.fullKey));
18   - }
19   - },
20   -};
packages/schema-table/editable.vue
... ... @@ -1,319 +0,0 @@
1   -<style>
2   -.z-table {
3   - width: 100%;
4   -}
5   -</style>
6   -
7   -<template>
8   - <div @keyup.enter="tableEditCell = {}">
9   - <div style="padding-bottom: 10px;">
10   - <el-button @click="handleNew" size="mini" v-if="!rowEdit">新增</el-button>
11   - <el-button @click="handleEdit" size="mini" v-if="!rowEditable && tableSelection.length > 0">编辑</el-button>
12   - <el-button @click="handleSave" size="mini" type="primary" v-if="rowEditable">完成</el-button>
13   - <el-button @click="handleCancel" size="mini" type="plain" v-if="rowNew && !rowEdit">取消</el-button>
14   - <el-button @click="handleDelete" size="mini" type="danger" v-if="tableSelection.length > 0">删除</el-button>
15   - </div>
16   - <el-table
17   - class="z-table"
18   - ref="table"
19   - :data="tableData"
20   - v-bind="{ size: 'small', ...tableProps }"
21   - v-on="tableEvents"
22   - @cell-dblclick="onCellDblclick"
23   - @selection-change="onSelectionChange"
24   - >
25   - <el-table-column type="selection" :selectable="r => (rowNew ? r.$new : true)"></el-table-column>
26   - <!-- 默认表格插槽 -->
27   - <slot name="default"></slot>
28   - <!-- 根据配置列表生成表格列 -->
29   - <template v-if="tableList && tableList.length > 0">
30   - <template v-for="(item, index) in tableList">
31   - <template v-if="bindItemVisible(item.visible)">
32   - <!-- 如果有表格列具名插槽 -->
33   - <slot v-if="$scopedSlots[item.keyPath.join('-')]" :name="item.keyPath.join('-')" v-bind="item"></slot>
34   - <!-- 默认表格列渲染 -->
35   - <el-table-column v-else v-bind="item" :prop="item.fullKey || item.key" :key="index" :min-width="minWidth || item.minWidth || item['min-width'] || (editable ? 140 : undefined)">
36   - <template #default="{ row, column, $index }">
37   - <cell-editable
38   - :editable="row.$editable || (editable && tableEditCell.index === row.$index && tableEditCell.key === (item.agentKey || item.fullKey || item.key))"
39   - :row="row"
40   - :item="item"
41   - @update="onCellUpdate"
42   - @done="onCellUpdateDone"
43   - @cancel="onCellUpdateCancel"
44   - :btn-visible="!row.$editable"
45   - >
46   - <!-- 如果有表格列值渲染具名插槽 -->
47   - <slot
48   - v-if="$scopedSlots[`value-${item.keyPath.join('-')}`]"
49   - :name="`value-${item.keyPath.join('-')}`"
50   - v-bind="item"
51   - :row="row"
52   - :value="$_get(row, item.fullKey)"
53   - :column="column"
54   - :index="$index"
55   - ></slot>
56   - <!-- 如果表格列配置了值渲染参数 -->
57   - <cell-value-render v-else-if="item.render" :row="row" :column="column" :index="$index" :item="item" />
58   - </cell-editable>
59   - </template>
60   - </el-table-column>
61   - </template>
62   - </template>
63   - </template>
64   - <!-- 已生成列表后的追加列插槽 -->
65   - <slot name="column-append"></slot>
66   - <!-- 末尾列插槽 -->
67   - <slot name="column-end"></slot>
68   - </el-table>
69   - </div>
70   -</template>
71   -
72   -<script>
73   -import { cloneDeep, get, set } from '../utils';
74   -import CellEditable from './cell-editable';
75   -import CellValueRender from './cell-value-render';
76   -
77   -const listHasKey = (list, name) => {
78   - let result = false;
79   - for (const row of list) {
80   - if (row[name]) {
81   - result = true;
82   - break;
83   - }
84   - }
85   - return result;
86   -};
87   -
88   -export default {
89   - name: 'TableNew',
90   - components: {
91   - CellEditable,
92   - CellValueRender,
93   - },
94   - props: {
95   - // 用于实例化本组件绑定v-model的值
96   - value: Array,
97   - // 配置列表
98   - list: {
99   - type: Array,
100   - required: true,
101   - },
102   - // 表格参数
103   - tableProps: {
104   - type: Object,
105   - default() {
106   - return {};
107   - },
108   - },
109   - // 表格事件
110   - tableEvents: Object,
111   - // 是否可编辑
112   - editable: Boolean,
113   - // 列宽
114   - minWidth: Number,
115   - },
116   - data() {
117   - return {
118   - tableList: [], // 表格配置列表
119   - tableData: [], // 表格数据
120   - tableRowTemplate: { $editable: true }, // 行数据模板
121   - tableEditCell: {}, // 正在编辑的单元格
122   - tableSelection: [], // 表格已选中
123   - };
124   - },
125   - computed: {
126   - // 表格实体
127   - instance: {
128   - get() {
129   - return this.$refs.table;
130   - },
131   - },
132   - // 行编辑状态
133   - rowEditable() {
134   - return listHasKey(this.tableData, '$editable');
135   - },
136   - // 存在新行
137   - rowNew() {
138   - return listHasKey(this.tableData, '$new');
139   - },
140   - // 存在编辑行
141   - rowEdit() {
142   - return listHasKey(this.tableData, '$edit');
143   - },
144   - },
145   - watch: {
146   - value: {
147   - handler(val = []) {
148   - this.tableData = val.map((o, i) => {
149   - return { ...o, $index: i, $editable: undefined, $new: undefined };
150   - });
151   - },
152   - immediate: true,
153   - },
154   - list: {
155   - handler(val) {
156   - // 深度克隆传入的列表,避免原始值被修改
157   - const newList = cloneDeep(this.list);
158   - // 生成列表值的全路径key,即列表项为对象时,对象内的key与上一级的key合并作为全路径key
159   - const generateFullKey = (list, parentKey) => {
160   - list.forEach(item => {
161   - if (item.group && item.list) {
162   - if (item.group.key) {
163   - item.fullKey = `${parentKey ? `${parentKey}.${item.group.key}` : item.group.key}`;
164   - } else {
165   - item.fullKey = parentKey || item.key;
166   - }
167   - generateFullKey(item.list, item.fullKey);
168   - } else {
169   - item.fullKey = `${parentKey ? `${parentKey}.${item.key}` : item.key}`;
170   - }
171   - });
172   - };
173   - // 生成fullKey
174   - generateFullKey(newList);
175   - // 创建输出列表
176   - const result = [];
177   - const tableRowTemplate = {};
178   - // 生成列表值的全路径key,即列表项为对象时,对象内的key与上一级的key合并作为全路径key
179   - const generateFlatList = list => {
180   - list.forEach(item => {
181   - if (item.group && item.list) {
182   - generateFlatList(item.list);
183   - } else if (!item.group && !item.list) {
184   - result.push({ ...item, keyPath: item.fullKey.split('.') });
185   - set(tableRowTemplate, item.fullKey, undefined);
186   - tableRowTemplate.$editable = true;
187   - }
188   - });
189   - };
190   - generateFlatList(newList);
191   - this.tableList = result;
192   - this.tableRowTemplate = tableRowTemplate;
193   - },
194   - immediate: true,
195   - },
196   - },
197   - methods: {
198   - $_get: get,
199   - // 处理新增逻辑
200   - handleNew() {
201   - const tableData = cloneDeep(this.tableData);
202   - tableData.push({ ...this.tableRowTemplate, $new: true });
203   - this.tableEditCell = {};
204   - this.tableData = tableData.map((o, i) => ({ ...o, $index: i }));
205   - },
206   - // 处理编辑逻辑
207   - handleEdit() {
208   - const tableData = cloneDeep(this.tableData);
209   - const selectionIndexArr = this.tableSelection.map(i => i.$index);
210   - tableData.forEach((r, i) => {
211   - if (selectionIndexArr.includes(r.$index)) {
212   - tableData[i].$editable = true;
213   - tableData[i].$edit = true;
214   - tableData[i].$new = true;
215   - }
216   - });
217   - this.tableEditCell = {};
218   - this.tableData = tableData.map((o, i) => ({ ...o, $index: i }));
219   - },
220   - // 处理保存逻辑
221   - handleSave() {
222   - const tableData = cloneDeep(this.tableData);
223   - tableData.forEach((r, i) => {
224   - delete tableData[i].$editable;
225   - });
226   - this.tableEditCell = {};
227   - this.tableData = tableData.map((o, i) => ({ ...o, $index: i }));
228   - this.$nextTick(() => {
229   - this.emitTableData(this.tableData);
230   - this.$emit(
231   - this.rowEdit ? 'row-edit' : 'row-new',
232   - this.tableData
233   - .filter(d => d.$new)
234   - .map(d => {
235   - delete d.$new;
236   - return d;
237   - }),
238   - );
239   - });
240   - },
241   - // 处理取消逻辑
242   - handleCancel() {
243   - let tableData = cloneDeep(this.tableData);
244   - tableData = tableData.filter(row => !row.$new);
245   - this.emitTableData(tableData);
246   - },
247   - // 处理删除逻辑
248   - handleDelete() {
249   - const tableData = cloneDeep(this.tableData);
250   - const selectionIndexArr = this.tableSelection.map(i => i.$index);
251   - if (!this.rowNew && !this.rowEdit) {
252   - this.$emit('row-delete', this.tableSelection);
253   - }
254   - this.tableEditCell = {};
255   - this.tableData = tableData.filter((d, i) => !selectionIndexArr.includes(i)).map((o, i) => ({ ...o, $index: i }));
256   - },
257   - // 更新表格数据
258   - emitTableData(tableData) {
259   - if (this.$listeners['input']) {
260   - this.$emit(
261   - 'input',
262   - tableData.map((o, i) => {
263   - return { ...o, $index: i, $editable: undefined, $new: undefined, $edit: undefined };
264   - }),
265   - );
266   - } else {
267   - this.tableData = tableData;
268   - }
269   - },
270   - // 绑定表格列显示隐藏状态
271   - bindItemVisible(visible = true) {
272   - let result = visible;
273   - if (typeof visible === 'function') {
274   - result = visible(this.tableData);
275   - }
276   - return result;
277   - },
278   - // 双击单元格
279   - onCellDblclick(row, column, cell, event) {
280   - if (this.editable && !this.rowNew) {
281   - this.tableEditCell = { index: row.$index, key: column.property };
282   - }
283   - },
284   - // 编辑表格更新值
285   - onCellUpdate({ oldValue, value, row, key, fullKey }) {
286   - this.setCellValue({ value, row, key, fullKey });
287   - },
288   - // 编辑表格确认
289   - onCellUpdateDone({ oldValue, value, row, key, fullKey }) {
290   - this.tableEditCell = {};
291   - if (this.$listeners['cell-edit']) {
292   - const { tableData } = this.setCellValue({ value, row, key, fullKey });
293   - this.emitTableData(tableData);
294   - this.$emit('cell-edit', { row, key, fullKey, value });
295   - }
296   - },
297   - // 表格取消编辑
298   - onCellUpdateCancel({ oldValue, value, row, key, fullKey }) {
299   - if (row.$new !== true) {
300   - this.setCellValue({ value: oldValue, row, key, fullKey });
301   - }
302   - },
303   - // 设置表格值
304   - setCellValue({ value, row, key, fullKey }) {
305   - const tableData = cloneDeep(this.tableData);
306   - const tableRow = tableData[row.$index];
307   - set(tableRow, fullKey, value);
308   - tableData[row.$index] = tableRow;
309   - this.$set(this.tableData, row.$index, tableRow);
310   - return { tableData, tableRow };
311   - },
312   - // 表格选中
313   - onSelectionChange(selection) {
314   - this.tableSelection = selection;
315   - this.$emit('selection', selection);
316   - },
317   - },
318   -};
319   -</script>
packages/schema-table/index.vue
1   -<style>
2   -.z-schema-table {
3   - width: 100%;
4   -}
5   -</style>
6   -
7   -<template>
8   - <el-table
9   - class="z-schema-table"
10   - ref="table"
11   - :data="tableData"
12   - v-bind="{ size, ...tableProps }"
13   - v-on="tableEvents"
14   - @select="onSelect"
15   - @select-all="onSelectAll"
16   - @selection-change="onSelectionChange"
17   - >
18   - <!-- 默认表格插槽 -->
19   - <slot name="default"></slot>
20   - <!-- 根据配置列表生成表格列 -->
21   - <template v-if="tableList && tableList.length > 0">
22   - <template v-for="(item, index) in tableList">
23   - <template v-if="bindItemVisible(item.visible)">
24   - <!-- 如果有表格列具名插槽 -->
25   - <slot v-if="$scopedSlots[item.keyPath.join('-')]" :name="item.keyPath.join('-')" v-bind="item"></slot>
26   - <!-- 默认表格列渲染 -->
27   - <el-table-column v-else v-bind="{ ...item, type: undefined }" :prop="item.fullKey || item.key" :key="index" :min-width="minWidth || item.minWidth || item['min-width']">
28   - <template #default="{ row, column, $index }">
29   - <cell-render :row="row" :item="item">
30   - <!-- 如果有表格列值渲染具名插槽 -->
31   - <slot
32   - v-if="$scopedSlots[`value-${item.keyPath.join('-')}`]"
33   - :name="`value-${item.keyPath.join('-')}`"
34   - v-bind="item"
35   - :row="row"
36   - :value="$_get(row, item.fullKey)"
37   - :column="column"
38   - :index="$index"
39   - ></slot>
40   - <!-- 如果表格列配置了值渲染参数 -->
41   - <cell-value-render v-else-if="item.render" :row="row" :column="column" :index="$index" :item="item" />
42   - </cell-render>
43   - </template>
44   - </el-table-column>
45   - </template>
46   - </template>
47   - </template>
48   - <!-- 已生成列表后的追加列插槽 -->
49   - <slot name="column-append"></slot>
50   - <!-- 末尾列插槽 -->
51   - <slot name="column-end"></slot>
52   - </el-table>
53   -</template>
54   -
55 1 <script>
56   -import { cloneDeep, get, set } from '../utils';
57   -import CellValueRender from './cell-value-render';
58   -
59 2 export default {
60 3 name: 'SchemaTable',
61   - components: {
62   - CellRender: {
63   - props: { row: Object, item: Object },
64   - render(h) {
65   - if (this.$scopedSlots.default) {
66   - return h('span', this.$scopedSlots.default());
67   - } else {
68   - return h('span', get(this.row, this.item.agentKey || this.item.fullKey));
69   - }
70   - },
71   - },
72   - CellValueRender,
73   - },
74 4 props: {
75   - // 用于实例化本组件绑定v-model的值
76   - value: Array,
77   - // 配置列表
78   - list: {
  5 + value: {
79 6 type: Array,
80   - required: true,
  7 + default() {
  8 + return [];
  9 + },
81 10 },
82   - // 表格参数
83   - tableProps: {
  11 + schema: {
84 12 type: Object,
85   - default: () => ({}),
86   - },
87   - // 表格事件
88   - tableEvents: Object,
89   - // 列宽
90   - minWidth: Number,
91   - // 大小
92   - size: {
93   - type: String,
94   - default: 'small',
  13 + required: true,
95 14 },
96 15 },
97 16 data() {
98 17 return {
99   - tableList: [], // 表格配置列表
100   - tableData: [], // 表格数据
  18 + model: this.value,
101 19 };
102 20 },
103   - computed: {
104   - // 表格实体
105   - instance: {
106   - get() {
107   - return this.$refs.table;
108   - },
109   - },
110   - },
111 21 watch: {
112   - value: {
113   - handler(val = []) {
114   - this.tableData = val;
115   - },
116   - immediate: true,
  22 + value(val = []) {
  23 + this.model = val;
117 24 },
118   - list: {
  25 + model: {
119 26 handler(val) {
120   - // 深度克隆传入的列表,避免原始值被修改
121   - const newList = cloneDeep(this.list);
122   - // 生成列表值的全路径key,即列表项为对象时,对象内的key与上一级的key合并作为全路径key
123   - const generateFullKey = (list, parentKey) => {
124   - list.forEach(item => {
125   - if (item.group && item.list) {
126   - if (item.group.key) {
127   - item.fullKey = `${parentKey ? `${parentKey}.${item.group.key}` : item.group.key}`;
128   - } else {
129   - item.fullKey = parentKey || item.key;
130   - }
131   - generateFullKey(item.list, item.fullKey);
132   - } else {
133   - item.fullKey = `${parentKey ? `${parentKey}.${item.key}` : item.key}`;
134   - }
135   - });
136   - };
137   - // 生成fullKey
138   - generateFullKey(newList);
139   - // 创建输出列表
140   - const result = [];
141   - // 生成列表值的全路径key,即列表项为对象时,对象内的key与上一级的key合并作为全路径key
142   - const generateFlatList = list => {
143   - list.forEach(item => {
144   - if (item.group && item.list) {
145   - generateFlatList(item.list);
146   - } else if (!item.group && !item.list) {
147   - result.push({ ...item, keyPath: item.fullKey.split('.') });
148   - }
149   - });
150   - };
151   - generateFlatList(newList);
152   - this.tableList = result;
  27 + this.$emit('input', val);
153 28 },
154   - immediate: true,
  29 + deep: true,
155 30 },
156 31 },
157   - methods: {
158   - $_get: get,
159   - // 绑定表格列显示隐藏状态
160   - bindItemVisible(visible = true) {
161   - let result = visible;
162   - if (typeof visible === 'function') {
163   - result = visible(this.tableData);
164   - }
165   - return result;
166   - },
167   - // 选中单行
168   - onSelect(selection, row) {
169   - if (selection.find(i => i.id === row.id)) {
170   - this.$emit('selection-change', [row], 'check');
171   - } else {
172   - this.$emit('selection-change', [row], 'uncheck');
173   - }
174   - },
175   - // 切换全选
176   - onSelectAll(selection) {
177   - if (selection && selection.length > 0) {
178   - this.$emit('selection-change', selection, 'check');
179   - } else {
180   - this.$emit('selection-change', this.tableData, 'uncheck');
181   - }
182   - },
183   - // 表格选中
184   - onSelectionChange(selection) {
185   - this.$emit('selection', selection);
186   - },
187   - // 切换某行选中状态
188   - toggleRowSelection(row, selected) {
189   - this.$refs.table && this.$refs.table.toggleRowSelection(row, selected);
190   - },
191   - // 清除表格选中
192   - clearSelection() {
193   - this.$refs.table && this.$refs.table.clearSelection();
194   - },
  32 + render(h) {
  33 + const schema = this.schema || {};
  34 + const _props = schema.props || {};
  35 + const _on = schema.on || {};
  36 + return h('z-table', { props: { value: this.model, columns: schema.items, ..._props }, on: _on, scopedSlots: this.$scopedSlots });
195 37 },
196 38 };
197 39 </script>
... ...
packages/select/index.bak.vue
... ... @@ -1,268 +0,0 @@
1   -<template>
2   - <el-select
3   - class="z-select"
4   - :disabled="disabled"
5   - :value-key="valueKey"
6   - :filterable="filterable"
7   - :remote="remote"
8   - :reserve-keyword="reserveKeyword"
9   - :clearable="clearable"
10   - :placeholder="placeholder"
11   - :remote-method="remoteMethod"
12   - :loading="loading"
13   - :size="size"
14   - :multiple="multiple"
15   - v-model="model"
16   - v-on="bindEvents"
17   - v-bind="selectProps"
18   - >
19   - <el-option v-for="item in optionsCurrent" :key="item.id" :label="labelFormat ? labelFormat(item) : item[labelKey]" :value="raw ? item : item[valueKey]" :disabled="item.disabled">
20   - <slot :item="item" :value="model"></slot>
21   - </el-option>
22   - <slot name="empty" slot="empty"></slot>
23   - <template slot="prefix">
24   - <i v-if="initing" class="el-icon-loading"></i>
25   - <slot v-else name="prefix"></slot>
26   - </template>
27   - </el-select>
28   -</template>
29   -
30   -<script>
31   -export default {
32   - name: 'Select',
33   - props: {
34   - value: [String, Number, Boolean, Object, Array],
35   - // 占位符
36   - placeholder: {
37   - type: String,
38   - default: '请选择',
39   - },
40   - // 选项数组
41   - options: {
42   - type: Array,
43   - default: () => [],
44   - },
45   - // 选中Label格式化方法
46   - labelFormat: Function,
47   - labelKey: {
48   - type: String,
49   - default: 'name',
50   - },
51   - valueKey: {
52   - type: String,
53   - default: 'code',
54   - },
55   - searchKey: {
56   - type: String,
57   - default: 'query',
58   - },
59   - size: {
60   - type: String,
61   - default: 'mini',
62   - },
63   - multiple: Boolean,
64   - disabled: Boolean,
65   - clearable: {
66   - type: Boolean,
67   - default: true,
68   - },
69   - filterable: {
70   - type: Boolean,
71   - default: true,
72   - },
73   - reserveKeyword: {
74   - type: Boolean,
75   - default: true,
76   - },
77   - selectProps: Object,
78   - // 保持原值,即绑定值为对象
79   - raw: Boolean,
80   - // 远程搜索URL
81   - url: String,
82   - // HTTP请求库
83   - http: Function,
84   - // 请求参数
85   - params: {
86   - type: Object,
87   - default: () => ({}),
88   - },
89   - // 请求配置
90   - config: {
91   - type: Object,
92   - default: () => ({}),
93   - },
94   - // 自定义接口
95   - queryApi: Function,
96   - // 触发远程搜索的字段长度
97   - triggerSize: {
98   - type: Number,
99   - default: 0,
100   - },
101   - auto: {
102   - type: Boolean,
103   - default: true,
104   - },
105   - update: Boolean,
106   - updateOnce: Boolean,
107   - beforeQuery: {
108   - type: Function,
109   - default: () => true,
110   - },
111   - },
112   - data() {
113   - return {
114   - model: this.value,
115   - optionsDataSource: this.fixOptions(this.options),
116   - optionsCurrent: this.fixOptions(this.options),
117   - loading: false,
118   - initing: false,
119   - loaded: false,
120   - };
121   - },
122   - created() {
123   - if (this.remote && this.auto) {
124   - this.initing = true;
125   - this.remoteMethod();
126   - }
127   - },
128   - watch: {
129   - value(val) {
130   - this.model = val;
131   - },
132   - options(val) {
133   - if (val) {
134   - this.optionsCurrent = this.fixOptions(this.optionsDataSource);
135   - }
136   - },
137   - },
138   - computed: {
139   - request() {
140   - return this.http || this.zHttp;
141   - },
142   - remote() {
143   - return Boolean(this.queryApi || (this.url && (this.http || this.zHttp)));
144   - },
145   - // Hack绑定事件,即令el-select组件绑定事件与当前z-select组件相同,且扩展部分事件返回值
146   - bindEvents() {
147   - let _events = {};
148   - Object.keys(this.$listeners || {}).forEach(key => {
149   - // 非绑定对象的情况下,通过change事件向上emit出当前选中项
150   - if (key === 'change' && !this.raw) {
151   - // 给el-select绑定change事件,且emit到z-select的change事件并拓展
152   - _events[key] = value => {
153   - this.$emit(
154   - key,
155   - value,
156   - this.multiple
157   - ? this.optionsCurrent.reduce((result, item) => {
158   - if (value.includes(item[this.valueKey])) {
159   - result.push(item);
160   - }
161   - return result;
162   - }, [])
163   - : this.optionsCurrent.find(item => item[this.valueKey] === value),
164   - );
165   - };
166   - } else {
167   - _events[key] = e => {
168   - this.$emit(key, e);
169   - };
170   - }
171   - });
172   - return {
173   - ..._events,
174   - 'visible-change': show => {
175   - if (this.remote && show) {
176   - if (this.updateOnce) {
177   - if (!this.loaded) {
178   - this.remoteMethod();
179   - }
180   - } else if (this.update) {
181   - this.remoteMethod();
182   - }
183   - }
184   - _events['visible-change'] && _events['visible-change'](show);
185   - },
186   - };
187   - },
188   - },
189   - methods: {
190   - // 修复当前数据源包含当前选中项
191   - fixOptions(list) {
192   - let fixOptions = [];
193   - if (this.raw) {
194   - if (this.multiple) {
195   - fixOptions = this.value && this.value.length > 0 ? [...this.value] : [];
196   - } else {
197   - fixOptions = this.value && Object.keys(this.value).length > 0 ? [this.value] : [];
198   - }
199   - }
200   - let hash = {};
201   - const options = [...fixOptions, ...this.options, ...list].reduce((result, item) => {
202   - if (!hash[item[this.valueKey]]) {
203   - // 如果当前元素的key值没有在hash对象里,则可放入最终结果数组
204   - hash[item[this.valueKey]] = true; // 把当前元素key值添加到hash对象
205   - item[this.valueKey] && item[this.labelKey] && result.push(item); // 把当前元素放入结果数组
206   - }
207   - return result; // 返回结果数组
208   - }, []);
209   - return options;
210   - },
211   - // 远程加载
212   - remoteMethod(val = '') {
213   - const searchText = val.trim();
214   - const isQueryValid = this.beforeQuery(searchText);
215   - if (isQueryValid) {
216   - if (searchText.length >= this.triggerSize) {
217   - this.loading = true;
218   - let requestPrimise;
219   - if (this.queryApi) {
220   - requestPrimise = this.queryApi(searchText);
221   - } else {
222   - requestPrimise = this.request({
223   - url: this.url,
224   - method: 'get',
225   - params: searchText ? { [this.searchKey]: searchText, ...this.params } : this.params,
226   - ...this.config,
227   - });
228   - }
229   - requestPrimise
230   - .then(res => {
231   - const response = res || {};
232   - const options = this.fixOptions(response.result);
233   - this.optionsDataSource = options;
234   - this.optionsCurrent = options;
235   - })
236   - .finally(() => {
237   - this.loading = false;
238   - this.initing = false;
239   - this.loaded = true;
240   - });
241   - } else {
242   - this.optionsCurrent = this.optionsDataSource.filter(item => {
243   - return item[this.labelKey].includes(val);
244   - });
245   - }
246   - } else {
247   - this.loading = false;
248   - this.initing = false;
249   - this.loaded = true;
250   - }
251   - },
252   - },
253   -};
254   -</script>
255   -
256   -<style lang="scss">
257   -.z-select {
258   - .el-input__prefix {
259   - height: 100%;
260   - display: flex;
261   - align-items: center;
262   - justify-content: center;
263   - .el-icon-loading {
264   - font-size: 16px;
265   - }
266   - }
267   -}
268   -</style>
packages/table/editable.vue
... ... @@ -27,23 +27,26 @@
27 27 </style>
28 28  
29 29 <template>
30   - <el-table :data="tableData | tableDataFilter" :size="tableSize" v-bind="_props" @header-click="onHeaderClick" @cell-click="onCellClick" @cell-dblclick="onCellDblclick">
  30 + <el-table :data="tableData | tableDataFilter" :size="tableSize" v-bind="bindProps" @header-click="onHeaderClick" @cell-click="onCellClick" @cell-dblclick="onCellDblclick">
31 31 <slot name="left"></slot>
32 32 <template v-for="(item, index) in columns">
33 33 <el-table-column v-bind="item" :key="index">
34 34 <slot :name="`header-${item.prop}`" slot="header"></slot>
35   - <template #default="{ row, column }">
  35 + <template #default="{ row, column, $index }">
36 36 <cell-editor
37   - :disabled="disabled || item.editable === false"
38   - :editable="item.editable !== false && (row.$editable || (tableEditCell.index === row.$index && tableEditCell.prop === item.prop))"
  37 + :disabled="editall || disabled || item.editable === false"
  38 + :editable="editall || (item.editable !== false && row.$editor && row.$editor.includes(item.prop))"
39 39 :component="item.component"
40 40 :value="row[column.property]"
41   - @input="onCellInput"
42   - @edit-click="setEditCell(row, column)"
43   - @edit-confirm="onEditConfirm"
  41 + @input="value => onCellInput(value, row, column, $index)"
  42 + @edit-click="setRowEditor(row, column, $index)"
  43 + @edit-confirm="value => onEditConfirm(value, row, column, $index)"
44 44 >
45   - <template v-if="$scopedSlots[`editor-${item.prop}`]">
46   - <slot :name="`editor-${item.prop}`" :value="row[column.property]" :onInput="onCellInput"></slot>
  45 + <template v-if="$scopedSlots[`editor-${item.prop}`]" slot="editor">
  46 + <slot :name="`editor-${item.prop}`" :value="row[column.property]" :onInput="value => onCellInput(value, row, column, $index)"></slot>
  47 + </template>
  48 + <template v-if="$scopedSlots[`cell-${item.prop}`]">
  49 + <slot :name="`cell-${item.prop}`" :value="row[column.property]" :row="row" :index="$index"></slot>
47 50 </template>
48 51 </cell-editor>
49 52 </template>
... ... @@ -72,7 +75,7 @@ export default {
72 75 },
73 76 watch: {
74 77 editable(val) {
75   - if (val && this.component === 'el-input') {
  78 + if (!this.disabled && val && this.component === 'el-input') {
76 79 this.$nextTick(() => {
77 80 this.$children[0] && this.$children[0].focus && this.$children[0].focus();
78 81 });
... ... @@ -91,8 +94,8 @@ export default {
91 94 },
92 95 }),
93 96 ];
94   - if (this.$scopedSlots.default) {
95   - editorRender = [this.$scopedSlots.default()];
  97 + if (this.$scopedSlots.editor) {
  98 + editorRender = [this.$scopedSlots.editor()];
96 99 }
97 100 if (!this.disabled) {
98 101 const handlerItems = [h('i', { attrs: { title: '确定', class: 'el-icon-check' }, on: { click: () => this.$emit('edit-confirm', this.value) } })];
... ... @@ -102,7 +105,10 @@ export default {
102 105 }
103 106 return h('span', { class: 'z-table-column__cell-editable' }, editorRender);
104 107 }
105   - const valueRender = [h('span', this.value)];
  108 + let valueRender = [h('span', this.value)];
  109 + if (this.$scopedSlots.default) {
  110 + valueRender = [this.$scopedSlots.default()];
  111 + }
106 112 if (!this.disabled) {
107 113 valueRender.push(h('i', { attrs: { title: '编辑', class: 'el-icon-edit' }, on: { click: () => this.$emit('edit-click') } }));
108 114 }
... ... @@ -123,6 +129,7 @@ export default {
123 129 return [];
124 130 },
125 131 },
  132 + editall: Boolean,
126 133 clickable: Boolean,
127 134 disabled: Boolean,
128 135 ...tableProps,
... ... @@ -134,13 +141,13 @@ export default {
134 141 data(val) {
135 142 this.tableData = val || [];
136 143 },
  144 + tableData(val) {
  145 + this.$emit('input', val || []);
  146 + },
137 147 },
138 148 data() {
139 149 return {
140 150 tableData: this.value,
141   - tableRowTemplate: { $editable: true }, // 行数据模板
142   - tableEditCell: {}, // 正在编辑的单元格
143   - tableSelection: [], // 表格已选中
144 151 };
145 152 },
146 153 filters: {
... ... @@ -156,32 +163,51 @@ export default {
156 163 },
157 164 onCellClick(row, column) {
158 165 if (this.clickable) {
159   - if (row.$index !== this.tableEditCell.index || column.property !== this.tableEditCell.prop) {
160   - this.tableEditCell = {};
161   - }
  166 + const prop = column.property;
  167 + let tableData = cloneDeep(this.tableData);
  168 + tableData.forEach((item, index) => {
  169 + if (!(index === row.$index && item.$editor && item.$editor.includes(prop))) {
  170 + item.$editor = [];
  171 + }
  172 + });
  173 + this.tableData = tableData;
162 174 }
163 175 },
164 176 onCellDblclick(row, column) {
165 177 if (this.clickable) {
166   - this.setEditCell(row, column);
  178 + this.setRowEditor(row, column, row.$index);
167 179 }
168 180 },
169   - setEditCell(row, column) {
170   - this.tableEditCell = { index: row.$index, prop: column.property };
  181 + setRowEditor(row, column, index) {
  182 + this.cancelEditCell();
  183 + let tableRow = this.tableData[index];
  184 + if (tableRow) {
  185 + if (tableRow.$editor) {
  186 + tableRow.$editor = [...tableRow.$editor, column.property];
  187 + } else {
  188 + tableRow.$editor = [column.property];
  189 + }
  190 + this.$set(this.tableData, index, tableRow);
  191 + }
171 192 },
172   - onEditConfirm(value) {
173   - this.$emit('cell-edit-confirm', { ...this.tableEditCell, value });
  193 + onEditConfirm(value, row, column, index) {
  194 + this.$emit('cell-edit-confirm', { row, index, prop: column.property, value });
174 195 this.cancelEditCell();
175 196 },
176 197 cancelEditCell() {
177   - this.tableEditCell = {};
  198 + this.tableData = this.tableData.map((item, index) => {
  199 + const newItem = cloneDeep(item);
  200 + delete newItem.$index;
  201 + delete newItem.$editor;
  202 + return newItem;
  203 + });
178 204 },
179   - onCellInput(value) {
  205 + onCellInput(value, row, column, index) {
180 206 const tableData = cloneDeep(this.tableData);
181   - const tableRow = tableData[this.tableEditCell.index];
182   - set(tableRow, this.tableEditCell.prop, value);
183   - tableData[this.tableEditCell.index] = tableRow;
184   - this.$set(this.tableData, this.tableEditCell.index, tableRow);
  207 + const tableRow = tableData[index];
  208 + set(tableRow, column.property, value);
  209 + tableData[index] = tableRow;
  210 + this.$set(this.tableData, index, tableRow);
185 211 },
186 212 },
187 213 };
... ...
packages/table/index.js
... ... @@ -16,6 +16,7 @@ export default {
16 16 },
17 17 },
18 18 editable: Boolean,
  19 + editall: Boolean,
19 20 clickable: Boolean,
20 21 disabled: Boolean,
21 22 ...tableProps,
... ...
packages/table/normal.vue
1 1 <template>
2   - <el-table :data="tableData" :size="tableSize" v-bind="_props">
  2 + <el-table :data="tableData" :size="tableSize" v-bind="bindProps">
3 3 <slot name="left"></slot>
4 4 <template v-for="(item, index) in columns">
5 5 <el-table-column v-bind="item" :key="index">
... ... @@ -7,6 +7,9 @@
7 7 <template v-if="$scopedSlots[`cell-${item.prop}`]" #default="{ row, column, $index }">
8 8 <slot :name="`cell-${item.prop}`" :value="row[column.property]" :row="row" :index="$index"></slot>
9 9 </template>
  10 + <template v-else-if="item.render && typeof item.render === 'function'" #default="{ row, column, $index }">
  11 + <cell-render :item="item" :value="row[item.prop]" :row="row" :column="column" :index="$index"></cell-render>
  12 + </template>
10 13 </el-table-column>
11 14 </template>
12 15 <slot></slot>
... ... @@ -19,6 +22,17 @@ import tableProps from &#39;./props&#39;;
19 22  
20 23 export default {
21 24 name: 'TableNormal',
  25 + components: {
  26 + CellRender: {
  27 + functional: true,
  28 + render(h, context) {
  29 + const props = context.props;
  30 + const item = props.item || {};
  31 + const content = item.render(props.value, props.row, h, props.index);
  32 + return typeof content === 'string' ? h('span', {}, [content]) : content;
  33 + },
  34 + },
  35 + },
22 36 inject: {
23 37 elForm: {
24 38 default: '',
... ... @@ -62,6 +76,16 @@ export default {
62 76 tableSize() {
63 77 return this.size || this._elFormItemSize || (this.elForm || {}).size || (this.$ELEMENT || {}).size;
64 78 },
  79 + bindProps() {
  80 + const tablePropsKeys = Object.keys(tableProps);
  81 + let props = {};
  82 + Object.keys(this._props).forEach(key => {
  83 + if (tablePropsKeys.includes(key)) {
  84 + props[key] = this._props[key];
  85 + }
  86 + });
  87 + return props;
  88 + },
65 89 },
66 90 };
67 91 </script>
... ...