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 通过配置JSON Schema的方式快速生成一个筛选表单 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 ```html 11 ```html
12 <template> 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 </template> 14 </template>
15 15
16 <script> 16 <script>
17 export default { 17 export default {
18 data() { 18 data() {
19 return { 19 return {
20 - tableData: [ 20 + model: [
21 { name: '张三', age: 16 }, 21 { name: '张三', age: 16 },
22 { name: '李四', age: 24 } 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,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 ```html 44 ```html
45 <template> 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 </template> 52 </template>
48 53
49 <script> 54 <script>
@@ -51,30 +56,35 @@ export default { @@ -51,30 +56,35 @@ export default {
51 data() { 56 data() {
52 return { 57 return {
53 tableData: [ 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 </script> 74 </script>
65 ``` 75 ```
66 76
67 ::: 77 :::
68 78
69 -::: snippet `slot`插槽式渲染 79 +## 列渲染
  80 +
  81 +除了使用插槽自定义列的内容之外,也支持直接在配置项中写渲染函数
  82 +
  83 +::: snippet 配置项中的`render`可以设置对应列单元格的渲染
70 84
71 ```html 85 ```html
72 <template> 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 </template> 88 </template>
79 89
80 <script> 90 <script>
@@ -82,32 +92,50 @@ export default { @@ -82,32 +92,50 @@ export default {
82 data() { 92 data() {
83 return { 93 return {
84 tableData: [ 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 </script> 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 ```html 122 ```html
109 <template> 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 </template> 139 </template>
112 140
113 <script> 141 <script>
@@ -115,22 +143,20 @@ export default { @@ -115,22 +143,20 @@ export default {
115 data() { 143 data() {
116 return { 144 return {
117 tableData: [ 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 </script> 161 </script>
136 ``` 162 ```
@@ -144,10 +170,7 @@ export default { @@ -144,10 +170,7 @@ export default {
144 参数|说明|类型|可选值|默认值 170 参数|说明|类型|可选值|默认值
145 -|-|-|-|- 171 -|-|-|-|-
146 value | 表格数据 | Array | - | - 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 ## Events 事件 175 ## Events 事件
153 176
examples/views/docs/component/table.md
@@ -132,15 +132,19 @@ export default { @@ -132,15 +132,19 @@ export default {
132 <div> 132 <div>
133 <z-form span="6"> 133 <z-form span="6">
134 <z-form-item>编辑模式:<el-switch v-model="editable"></el-switch></z-form-item> 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 <z-form-item>双击编辑:<el-switch v-model="clickable"></el-switch></z-form-item> 136 <z-form-item>双击编辑:<el-switch v-model="clickable"></el-switch></z-form-item>
136 </z-form> 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 <template #editor-gender="{ value, onInput, index }"> 139 <template #editor-gender="{ value, onInput, index }">
139 <el-radio-group size="mini" :value="value" @input="onInput"> 140 <el-radio-group size="mini" :value="value" @input="onInput">
140 <el-radio-button label="男">男</el-radio-button> 141 <el-radio-button label="男">男</el-radio-button>
141 <el-radio-button label="女">女</el-radio-button> 142 <el-radio-button label="女">女</el-radio-button>
142 </el-radio-group> 143 </el-radio-group>
143 </template> 144 </template>
  145 + <template #cell-gender="{ value }">
  146 + <el-tag size="mini" type="info" disable-transitions>{{ value }}</el-tag>
  147 + </template>
144 <el-table-column label="操作" width="80"> 148 <el-table-column label="操作" width="80">
145 <template #default="{ $index }"> 149 <template #default="{ $index }">
146 <el-button type="text" @click="deleteRow($index)">删除</el-button> 150 <el-button type="text" @click="deleteRow($index)">删除</el-button>
@@ -154,6 +158,7 @@ export default { @@ -154,6 +158,7 @@ export default {
154 export default { 158 export default {
155 data() { 159 data() {
156 return { 160 return {
  161 + editall: false,
157 editable: true, 162 editable: true,
158 clickable: true, 163 clickable: true,
159 tableData: [ 164 tableData: [
packages/schema-table/cell-editable.vue
@@ -1,90 +0,0 @@ @@ -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,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,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 <script> 1 <script>
56 -import { cloneDeep, get, set } from '../utils';  
57 -import CellValueRender from './cell-value-render';  
58 -  
59 export default { 2 export default {
60 name: 'SchemaTable', 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 props: { 4 props: {
75 - // 用于实例化本组件绑定v-model的值  
76 - value: Array,  
77 - // 配置列表  
78 - list: { 5 + value: {
79 type: Array, 6 type: Array,
80 - required: true, 7 + default() {
  8 + return [];
  9 + },
81 }, 10 },
82 - // 表格参数  
83 - tableProps: { 11 + schema: {
84 type: Object, 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 data() { 16 data() {
98 return { 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 watch: { 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 handler(val) { 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 </script> 39 </script>
packages/select/index.bak.vue
@@ -1,268 +0,0 @@ @@ -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,23 +27,26 @@
27 </style> 27 </style>
28 28
29 <template> 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 <slot name="left"></slot> 31 <slot name="left"></slot>
32 <template v-for="(item, index) in columns"> 32 <template v-for="(item, index) in columns">
33 <el-table-column v-bind="item" :key="index"> 33 <el-table-column v-bind="item" :key="index">
34 <slot :name="`header-${item.prop}`" slot="header"></slot> 34 <slot :name="`header-${item.prop}`" slot="header"></slot>
35 - <template #default="{ row, column }"> 35 + <template #default="{ row, column, $index }">
36 <cell-editor 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 :component="item.component" 39 :component="item.component"
40 :value="row[column.property]" 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 </template> 50 </template>
48 </cell-editor> 51 </cell-editor>
49 </template> 52 </template>
@@ -72,7 +75,7 @@ export default { @@ -72,7 +75,7 @@ export default {
72 }, 75 },
73 watch: { 76 watch: {
74 editable(val) { 77 editable(val) {
75 - if (val && this.component === 'el-input') { 78 + if (!this.disabled && val && this.component === 'el-input') {
76 this.$nextTick(() => { 79 this.$nextTick(() => {
77 this.$children[0] && this.$children[0].focus && this.$children[0].focus(); 80 this.$children[0] && this.$children[0].focus && this.$children[0].focus();
78 }); 81 });
@@ -91,8 +94,8 @@ export default { @@ -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 if (!this.disabled) { 100 if (!this.disabled) {
98 const handlerItems = [h('i', { attrs: { title: '确定', class: 'el-icon-check' }, on: { click: () => this.$emit('edit-confirm', this.value) } })]; 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,7 +105,10 @@ export default {
102 } 105 }
103 return h('span', { class: 'z-table-column__cell-editable' }, editorRender); 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 if (!this.disabled) { 112 if (!this.disabled) {
107 valueRender.push(h('i', { attrs: { title: '编辑', class: 'el-icon-edit' }, on: { click: () => this.$emit('edit-click') } })); 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,6 +129,7 @@ export default {
123 return []; 129 return [];
124 }, 130 },
125 }, 131 },
  132 + editall: Boolean,
126 clickable: Boolean, 133 clickable: Boolean,
127 disabled: Boolean, 134 disabled: Boolean,
128 ...tableProps, 135 ...tableProps,
@@ -134,13 +141,13 @@ export default { @@ -134,13 +141,13 @@ export default {
134 data(val) { 141 data(val) {
135 this.tableData = val || []; 142 this.tableData = val || [];
136 }, 143 },
  144 + tableData(val) {
  145 + this.$emit('input', val || []);
  146 + },
137 }, 147 },
138 data() { 148 data() {
139 return { 149 return {
140 tableData: this.value, 150 tableData: this.value,
141 - tableRowTemplate: { $editable: true }, // 行数据模板  
142 - tableEditCell: {}, // 正在编辑的单元格  
143 - tableSelection: [], // 表格已选中  
144 }; 151 };
145 }, 152 },
146 filters: { 153 filters: {
@@ -156,32 +163,51 @@ export default { @@ -156,32 +163,51 @@ export default {
156 }, 163 },
157 onCellClick(row, column) { 164 onCellClick(row, column) {
158 if (this.clickable) { 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 onCellDblclick(row, column) { 176 onCellDblclick(row, column) {
165 if (this.clickable) { 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 this.cancelEditCell(); 195 this.cancelEditCell();
175 }, 196 },
176 cancelEditCell() { 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 const tableData = cloneDeep(this.tableData); 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,6 +16,7 @@ export default {
16 }, 16 },
17 }, 17 },
18 editable: Boolean, 18 editable: Boolean,
  19 + editall: Boolean,
19 clickable: Boolean, 20 clickable: Boolean,
20 disabled: Boolean, 21 disabled: Boolean,
21 ...tableProps, 22 ...tableProps,
packages/table/normal.vue
1 <template> 1 <template>
2 - <el-table :data="tableData" :size="tableSize" v-bind="_props"> 2 + <el-table :data="tableData" :size="tableSize" v-bind="bindProps">
3 <slot name="left"></slot> 3 <slot name="left"></slot>
4 <template v-for="(item, index) in columns"> 4 <template v-for="(item, index) in columns">
5 <el-table-column v-bind="item" :key="index"> 5 <el-table-column v-bind="item" :key="index">
@@ -7,6 +7,9 @@ @@ -7,6 +7,9 @@
7 <template v-if="$scopedSlots[`cell-${item.prop}`]" #default="{ row, column, $index }"> 7 <template v-if="$scopedSlots[`cell-${item.prop}`]" #default="{ row, column, $index }">
8 <slot :name="`cell-${item.prop}`" :value="row[column.property]" :row="row" :index="$index"></slot> 8 <slot :name="`cell-${item.prop}`" :value="row[column.property]" :row="row" :index="$index"></slot>
9 </template> 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 </el-table-column> 13 </el-table-column>
11 </template> 14 </template>
12 <slot></slot> 15 <slot></slot>
@@ -19,6 +22,17 @@ import tableProps from &#39;./props&#39;; @@ -19,6 +22,17 @@ import tableProps from &#39;./props&#39;;
19 22
20 export default { 23 export default {
21 name: 'TableNormal', 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 inject: { 36 inject: {
23 elForm: { 37 elForm: {
24 default: '', 38 default: '',
@@ -62,6 +76,16 @@ export default { @@ -62,6 +76,16 @@ export default {
62 tableSize() { 76 tableSize() {
63 return this.size || this._elFormItemSize || (this.elForm || {}).size || (this.$ELEMENT || {}).size; 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 </script> 91 </script>