Commit 004eaf476b3e0714f377588fc27957ebae30727a
1 parent
86a49e4b
Exists in
master
and in
1 other branch
新式表格分离可编辑组件
Showing
4 changed files
with
342 additions
and
183 deletions
Show diff stats
examples/views/page/other.vue
| @@ -39,7 +39,7 @@ | @@ -39,7 +39,7 @@ | ||
| 39 | 39 | ||
| 40 | <script> | 40 | <script> |
| 41 | import EgForm from './form-new'; | 41 | import EgForm from './form-new'; |
| 42 | -import EgTable from './table-new'; | 42 | +import EgTable from './table-new/editable'; |
| 43 | 43 | ||
| 44 | export default { | 44 | export default { |
| 45 | name: 'other', | 45 | name: 'other', |
examples/views/page/table-new/cell-editable.vue
| @@ -63,6 +63,7 @@ export default { | @@ -63,6 +63,7 @@ export default { | ||
| 63 | if (val) { | 63 | if (val) { |
| 64 | this.confirm = false; | 64 | this.confirm = false; |
| 65 | this.oldValue = get(this.row, this.item.agentKey || this.item.fullKey); | 65 | this.oldValue = get(this.row, this.item.agentKey || this.item.fullKey); |
| 66 | + this.value = get(this.row, this.item.agentKey || this.item.fullKey); | ||
| 66 | } else { | 67 | } else { |
| 67 | if (!this.confirm) { | 68 | if (!this.confirm) { |
| 68 | this.$emit('cancel', this.emitData); | 69 | this.$emit('cancel', this.emitData); |
| @@ -78,7 +79,7 @@ export default { | @@ -78,7 +79,7 @@ export default { | ||
| 78 | }, | 79 | }, |
| 79 | methods: { | 80 | methods: { |
| 80 | $_get: get, | 81 | $_get: get, |
| 81 | - // 组件出发input事件 | 82 | + // 组件触发input事件 |
| 82 | onInput(value) { | 83 | onInput(value) { |
| 83 | this.value = value; | 84 | this.value = value; |
| 84 | this.$emit('update', this.emitData) | 85 | this.$emit('update', this.emitData) |
| @@ -0,0 +1,293 @@ | @@ -0,0 +1,293 @@ | ||
| 1 | +<style> | ||
| 2 | +.eagle-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 class="eagle-table" ref="table" :data="tableData" v-bind="{ size: 'small', ...tableProps }" | ||
| 17 | + v-on="tableEvents" @cell-dblclick="onCellDblclick" @selection-change="onSelectionChange" | ||
| 18 | + > | ||
| 19 | + <el-table-column type="selection" :selectable="r => rowNew ? r.$new : true"></el-table-column> | ||
| 20 | + <!-- 默认表格插槽 --> | ||
| 21 | + <slot name="default"></slot> | ||
| 22 | + <!-- 根据配置列表生成表格列 --> | ||
| 23 | + <template v-if="tableList && tableList.length > 0"> | ||
| 24 | + <template v-for="(item, index) in tableList"> | ||
| 25 | + <template v-if="bindItemVisible(item.visible)"> | ||
| 26 | + <!-- 如果有表格列具名插槽 --> | ||
| 27 | + <slot v-if="$scopedSlots[item.keyPath.join('-')]" :name="item.keyPath.join('-')" v-bind="item"></slot> | ||
| 28 | + <!-- 默认表格列渲染 --> | ||
| 29 | + <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)"> | ||
| 30 | + <template #default="{ row, column, $index }"> | ||
| 31 | + <cell-editable | ||
| 32 | + :editable="row.$editable || (editable && (tableEditCell.index === row.$index && tableEditCell.key === (item.agentKey || item.fullKey || item.key)))" | ||
| 33 | + :row="row" :item="item" @update="onCellUpdate" @done="onCellUpdateDone" @cancel="onCellUpdateCancel" :btn-visible="!row.$editable" | ||
| 34 | + > | ||
| 35 | + <!-- 如果有表格列值渲染具名插槽 --> | ||
| 36 | + <slot v-if="$scopedSlots[`value-${item.keyPath.join('-')}`]" :name="`value-${item.keyPath.join('-')}`" v-bind="item" | ||
| 37 | + :row="row" :value="$_get(row, item.fullKey)" :column="column" :index="$index" | ||
| 38 | + ></slot> | ||
| 39 | + <!-- 如果表格列配置了值渲染参数 --> | ||
| 40 | + <component v-else-if="item.render" :is="item.render.type" v-bind="item.render.props" :style="item.render.style"> | ||
| 41 | + <template v-if="item.render.children">{{ bindItemRenderChildren(item, { row, column, $index }) }}</template> | ||
| 42 | + <template v-else>{{ $_get(row, item.fullKey) }}</template> | ||
| 43 | + </component> | ||
| 44 | + </cell-editable> | ||
| 45 | + </template> | ||
| 46 | + </el-table-column> | ||
| 47 | + </template> | ||
| 48 | + </template> | ||
| 49 | + </template> | ||
| 50 | + <!-- 已生成列表后的追加列插槽 --> | ||
| 51 | + <slot name="column-append"></slot> | ||
| 52 | + <!-- 末尾列插槽 --> | ||
| 53 | + <slot name="column-end"></slot> | ||
| 54 | + </el-table> | ||
| 55 | + </div> | ||
| 56 | +</template> | ||
| 57 | + | ||
| 58 | +<script> | ||
| 59 | +import { cloneDeep, get, set } from '../form-new/util'; | ||
| 60 | +import CellEditable from './cell-editable'; | ||
| 61 | + | ||
| 62 | +const listHasKey = (list, name) => { | ||
| 63 | + let result = false; | ||
| 64 | + for (const row of list) { | ||
| 65 | + if (row[name]) { | ||
| 66 | + result = true; | ||
| 67 | + break; | ||
| 68 | + } | ||
| 69 | + } | ||
| 70 | + return result; | ||
| 71 | +} | ||
| 72 | + | ||
| 73 | +export default { | ||
| 74 | + name: 'TableNew', | ||
| 75 | + components: { CellEditable }, | ||
| 76 | + props: { | ||
| 77 | + // 用于实例化本组件绑定v-model的值 | ||
| 78 | + value: Array, | ||
| 79 | + // 配置列表 | ||
| 80 | + list: { | ||
| 81 | + type: Array, | ||
| 82 | + required: true | ||
| 83 | + }, | ||
| 84 | + // 表格参数 | ||
| 85 | + tableProps: { | ||
| 86 | + type: Object, | ||
| 87 | + default() { return {} } | ||
| 88 | + }, | ||
| 89 | + // 表格事件 | ||
| 90 | + tableEvents: Object, | ||
| 91 | + // 是否可编辑 | ||
| 92 | + editable: Boolean, | ||
| 93 | + // 列宽 | ||
| 94 | + minWidth: Number, | ||
| 95 | + }, | ||
| 96 | + data() { | ||
| 97 | + return { | ||
| 98 | + tableList: [], // 表格配置列表 | ||
| 99 | + tableData: [], // 表格数据 | ||
| 100 | + tableRowTemplate: { $editable: true }, // 行数据模板 | ||
| 101 | + tableEditCell: {}, // 正在编辑的单元格 | ||
| 102 | + tableSelection: [], // 表格已选中 | ||
| 103 | + }; | ||
| 104 | + }, | ||
| 105 | + computed: { | ||
| 106 | + // 表格实体 | ||
| 107 | + instance: { | ||
| 108 | + get() { return this.$refs.table; } | ||
| 109 | + }, | ||
| 110 | + // 行编辑状态 | ||
| 111 | + rowEditable() { | ||
| 112 | + return listHasKey(this.tableData, '$editable'); | ||
| 113 | + }, | ||
| 114 | + // 存在新行 | ||
| 115 | + rowNew() { | ||
| 116 | + return listHasKey(this.tableData, '$new'); | ||
| 117 | + }, | ||
| 118 | + // 存在编辑行 | ||
| 119 | + rowEdit() { | ||
| 120 | + return listHasKey(this.tableData, '$edit'); | ||
| 121 | + } | ||
| 122 | + }, | ||
| 123 | + watch: { | ||
| 124 | + value: { | ||
| 125 | + handler(val = []) { | ||
| 126 | + this.tableData = val.map((o, i) => { | ||
| 127 | + return { ...o, $index: i, $editable: undefined, $new: undefined }; | ||
| 128 | + }); | ||
| 129 | + }, | ||
| 130 | + immediate: true | ||
| 131 | + }, | ||
| 132 | + list: { | ||
| 133 | + handler(val) { | ||
| 134 | + // 深度克隆传入的列表,避免原始值被修改 | ||
| 135 | + const newList = cloneDeep(this.list); | ||
| 136 | + // 生成列表值的全路径key,即列表项为对象时,对象内的key与上一级的key合并作为全路径key | ||
| 137 | + const generateFullKey = (list, parentKey) => { | ||
| 138 | + list.forEach(item => { | ||
| 139 | + if (item.group && item.list) { | ||
| 140 | + if (item.group.key) { | ||
| 141 | + item.fullKey = `${parentKey ? `${parentKey}.${item.group.key}` : item.group.key}`; | ||
| 142 | + } else { | ||
| 143 | + item.fullKey = parentKey || item.key; | ||
| 144 | + } | ||
| 145 | + generateFullKey(item.list, item.fullKey); | ||
| 146 | + } else { | ||
| 147 | + item.fullKey = `${parentKey ? `${parentKey}.${item.key}` : item.key}`; | ||
| 148 | + } | ||
| 149 | + }); | ||
| 150 | + }; | ||
| 151 | + // 生成fullKey | ||
| 152 | + generateFullKey(newList); | ||
| 153 | + // 创建输出列表 | ||
| 154 | + const result = []; | ||
| 155 | + const tableRowTemplate = {}; | ||
| 156 | + // 生成列表值的全路径key,即列表项为对象时,对象内的key与上一级的key合并作为全路径key | ||
| 157 | + const generateFlatList = (list) => { | ||
| 158 | + list.forEach(item => { | ||
| 159 | + if (item.group && item.list) { | ||
| 160 | + generateFlatList(item.list); | ||
| 161 | + } else if (!item.group && !item.list) { | ||
| 162 | + result.push({ ...item, keyPath: item.fullKey.split('.') }); | ||
| 163 | + set(tableRowTemplate, item.fullKey, undefined); | ||
| 164 | + tableRowTemplate.$editable = true; | ||
| 165 | + } | ||
| 166 | + }); | ||
| 167 | + }; | ||
| 168 | + generateFlatList(newList); | ||
| 169 | + this.tableList = result; | ||
| 170 | + this.tableRowTemplate = tableRowTemplate; | ||
| 171 | + }, | ||
| 172 | + immediate: true | ||
| 173 | + } | ||
| 174 | + }, | ||
| 175 | + methods: { | ||
| 176 | + $_get: get, | ||
| 177 | + // 处理新增逻辑 | ||
| 178 | + handleNew() { | ||
| 179 | + const tableData = cloneDeep(this.tableData); | ||
| 180 | + tableData.push({ ...this.tableRowTemplate, $new: true }); | ||
| 181 | + this.tableEditCell = {}; | ||
| 182 | + this.tableData = tableData.map((o, i) => ({ ...o, $index: i })); | ||
| 183 | + }, | ||
| 184 | + // 处理编辑逻辑 | ||
| 185 | + handleEdit() { | ||
| 186 | + const tableData = cloneDeep(this.tableData); | ||
| 187 | + const selectionIndexArr = this.tableSelection.map(i => i.$index); | ||
| 188 | + tableData.forEach((r, i) => { | ||
| 189 | + if (selectionIndexArr.includes(r.$index)) { | ||
| 190 | + tableData[i].$editable = true; | ||
| 191 | + tableData[i].$edit = true; | ||
| 192 | + tableData[i].$new = true; | ||
| 193 | + } | ||
| 194 | + }); | ||
| 195 | + this.tableEditCell = {}; | ||
| 196 | + this.tableData = tableData.map((o, i) => ({ ...o, $index: i })); | ||
| 197 | + }, | ||
| 198 | + // 处理保存逻辑 | ||
| 199 | + handleSave() { | ||
| 200 | + const tableData = cloneDeep(this.tableData); | ||
| 201 | + tableData.forEach((r, i) => { | ||
| 202 | + delete tableData[i].$editable; | ||
| 203 | + }); | ||
| 204 | + this.tableEditCell = {}; | ||
| 205 | + this.tableData = tableData.map((o, i) => ({ ...o, $index: i })); | ||
| 206 | + this.$nextTick(() => { | ||
| 207 | + this.emitTableData(this.tableData); | ||
| 208 | + this.$emit(this.rowEdit ? 'row-edit' : 'row-new', this.tableData.filter(d => d.$new).map(d => { | ||
| 209 | + delete d.$new; | ||
| 210 | + return d; | ||
| 211 | + })); | ||
| 212 | + }); | ||
| 213 | + }, | ||
| 214 | + // 处理取消逻辑 | ||
| 215 | + handleCancel() { | ||
| 216 | + let tableData = cloneDeep(this.tableData); | ||
| 217 | + tableData = tableData.filter(row => !row.$new); | ||
| 218 | + this.emitTableData(tableData); | ||
| 219 | + }, | ||
| 220 | + // 处理删除逻辑 | ||
| 221 | + handleDelete() { | ||
| 222 | + const tableData = cloneDeep(this.tableData); | ||
| 223 | + const selectionIndexArr = this.tableSelection.map(i => i.$index); | ||
| 224 | + if (!this.rowNew && !this.rowEdit) { | ||
| 225 | + this.$emit('row-delete', this.tableSelection); | ||
| 226 | + } | ||
| 227 | + this.tableEditCell = {}; | ||
| 228 | + this.tableData = tableData.filter((d, i) => !selectionIndexArr.includes(i)).map((o, i) => ({ ...o, $index: i })); | ||
| 229 | + }, | ||
| 230 | + // 更新表格数据 | ||
| 231 | + emitTableData(tableData) { | ||
| 232 | + if (this.$listeners['input']) { | ||
| 233 | + this.$emit('input', tableData.map((o, i) => { | ||
| 234 | + return { ...o, $index: i, $editable: undefined, $new: undefined, $edit: undefined }; | ||
| 235 | + })); | ||
| 236 | + } else { | ||
| 237 | + this.tableData = tableData; | ||
| 238 | + } | ||
| 239 | + }, | ||
| 240 | + // 绑定表格列显示隐藏状态 | ||
| 241 | + bindItemVisible(visible = true) { | ||
| 242 | + let result = visible; | ||
| 243 | + if (typeof visible === 'function') { | ||
| 244 | + result = visible(this.tableData); | ||
| 245 | + } | ||
| 246 | + return result; | ||
| 247 | + }, | ||
| 248 | + // 处理表格项渲染时的子值 | ||
| 249 | + bindItemRenderChildren(item, scope) { | ||
| 250 | + return item.render.children instanceof Function ? item.render.children(scope) : this.$_get(scope.row, item.fullKey) | ||
| 251 | + }, | ||
| 252 | + // 双击单元格 | ||
| 253 | + onCellDblclick(row, column, cell, event) { | ||
| 254 | + if (this.editable && !this.rowNew) { | ||
| 255 | + this.tableEditCell = { index: row.$index, key: column.property }; | ||
| 256 | + } | ||
| 257 | + }, | ||
| 258 | + // 编辑表格更新值 | ||
| 259 | + onCellUpdate({ oldValue, value, row, key, fullKey }) { | ||
| 260 | + this.setCellValue({ value, row, key, fullKey }); | ||
| 261 | + }, | ||
| 262 | + // 编辑表格确认 | ||
| 263 | + onCellUpdateDone({ oldValue, value, row, key, fullKey }) { | ||
| 264 | + this.tableEditCell = {}; | ||
| 265 | + if (this.$listeners['cell-edit']) { | ||
| 266 | + const { tableData } = this.setCellValue({ value, row, key, fullKey }); | ||
| 267 | + this.emitTableData(tableData); | ||
| 268 | + this.$emit('cell-edit', { row, key, fullKey, value }); | ||
| 269 | + } | ||
| 270 | + }, | ||
| 271 | + // 表格取消编辑 | ||
| 272 | + onCellUpdateCancel({ oldValue, value, row, key, fullKey }) { | ||
| 273 | + if (row.$new !== true) { | ||
| 274 | + this.setCellValue({ value: oldValue, row, key, fullKey }); | ||
| 275 | + } | ||
| 276 | + }, | ||
| 277 | + // 设置表格值 | ||
| 278 | + setCellValue({ value, row, key, fullKey }) { | ||
| 279 | + const tableData = cloneDeep(this.tableData); | ||
| 280 | + const tableRow = tableData[row.$index]; | ||
| 281 | + set(tableRow, fullKey, value); | ||
| 282 | + tableData[row.$index] = tableRow; | ||
| 283 | + this.$set(this.tableData, row.$index, tableRow); | ||
| 284 | + return { tableData, tableRow }; | ||
| 285 | + }, | ||
| 286 | + // 表格选中 | ||
| 287 | + onSelectionChange(selection) { | ||
| 288 | + this.tableSelection = selection; | ||
| 289 | + this.$emit('selection', selection); | ||
| 290 | + } | ||
| 291 | + } | ||
| 292 | +}; | ||
| 293 | +</script> | ||
| 0 | \ No newline at end of file | 294 | \ No newline at end of file |
examples/views/page/table-new/index.vue
| @@ -5,74 +5,60 @@ | @@ -5,74 +5,60 @@ | ||
| 5 | </style> | 5 | </style> |
| 6 | 6 | ||
| 7 | <template> | 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 class="eagle-table" ref="table" :data="tableData" v-bind="{ size: 'small', ...tableProps }" | ||
| 17 | - v-on="tableEvents" @cell-dblclick="onCellDblclick" @selection-change="onSelectionChange" | ||
| 18 | - > | ||
| 19 | - <el-table-column type="selection" :selectable="r => rowNew ? r.$new : true"></el-table-column> | ||
| 20 | - <!-- 默认表格插槽 --> | ||
| 21 | - <slot name="default"></slot> | ||
| 22 | - <!-- 根据配置列表生成表格列 --> | ||
| 23 | - <template v-if="tableList && tableList.length > 0"> | ||
| 24 | - <template v-for="(item, index) in tableList"> | ||
| 25 | - <template v-if="bindItemVisible(item.visible)"> | ||
| 26 | - <!-- 如果有表格列具名插槽 --> | ||
| 27 | - <slot v-if="$scopedSlots[item.keyPath.join('-')]" :name="item.keyPath.join('-')" v-bind="item"></slot> | ||
| 28 | - <!-- 默认表格列渲染 --> | ||
| 29 | - <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)"> | ||
| 30 | - <template #default="{ row, column, $index }"> | ||
| 31 | - <cell-editable | ||
| 32 | - :editable="row.$editable || (editable && (tableEditCell.index === row.$index && tableEditCell.key === (item.agentKey || item.fullKey || item.key)))" | ||
| 33 | - :row="row" :item="item" @update="onCellUpdate" @done="onCellUpdateDone" @cancel="onCellUpdateCancel" :btn-visible="!row.$editable" | ||
| 34 | - > | ||
| 35 | - <!-- 如果有表格列值渲染具名插槽 --> | ||
| 36 | - <slot v-if="$scopedSlots[`value-${item.keyPath.join('-')}`]" :name="`value-${item.keyPath.join('-')}`" v-bind="item" | ||
| 37 | - :row="row" :value="$_get(row, item.fullKey)" :column="column" :index="$index" | ||
| 38 | - ></slot> | ||
| 39 | - <!-- 如果表格列配置了值渲染参数 --> | ||
| 40 | - <component v-else-if="item.render" :is="item.render.type" v-bind="item.render.props" :style="item.render.style"> | ||
| 41 | - <template v-if="item.render.children">{{ bindItemRenderChildren(item, { row, column, $index }) }}</template> | ||
| 42 | - <template v-else>{{ $_get(row, item.fullKey) }}</template> | ||
| 43 | - </component> | ||
| 44 | - </cell-editable> | ||
| 45 | - </template> | ||
| 46 | - </el-table-column> | ||
| 47 | - </template> | 8 | + <el-table class="eagle-table" ref="table" :data="tableData" v-bind="{ size: 'small', ...tableProps }" |
| 9 | + v-on="tableEvents" @selection-change="onSelectionChange" | ||
| 10 | + > | ||
| 11 | + <!-- 默认表格插槽 --> | ||
| 12 | + <slot name="default"></slot> | ||
| 13 | + <!-- 根据配置列表生成表格列 --> | ||
| 14 | + <template v-if="tableList && tableList.length > 0"> | ||
| 15 | + <template v-for="(item, index) in tableList"> | ||
| 16 | + <template v-if="bindItemVisible(item.visible)"> | ||
| 17 | + <!-- 如果有表格列具名插槽 --> | ||
| 18 | + <slot v-if="$scopedSlots[item.keyPath.join('-')]" :name="item.keyPath.join('-')" v-bind="item"></slot> | ||
| 19 | + <!-- 默认表格列渲染 --> | ||
| 20 | + <el-table-column v-else v-bind="item" :prop="item.fullKey || item.key" :key="index" :min-width="minWidth || item.minWidth || item['min-width']"> | ||
| 21 | + <template #default="{ row, column, $index }"> | ||
| 22 | + <cell-render :row="row" :item="item"> | ||
| 23 | + <!-- 如果有表格列值渲染具名插槽 --> | ||
| 24 | + <slot v-if="$scopedSlots[`value-${item.keyPath.join('-')}`]" :name="`value-${item.keyPath.join('-')}`" v-bind="item" | ||
| 25 | + :row="row" :value="$_get(row, item.fullKey)" :column="column" :index="$index" | ||
| 26 | + ></slot> | ||
| 27 | + <!-- 如果表格列配置了值渲染参数 --> | ||
| 28 | + <component v-else-if="item.render" :is="item.render.type" v-bind="item.render.props" :style="item.render.style"> | ||
| 29 | + <template v-if="item.render.children">{{ bindItemRenderChildren(item, { row, column, $index }) }}</template> | ||
| 30 | + <template v-else>{{ $_get(row, item.fullKey) }}</template> | ||
| 31 | + </component> | ||
| 32 | + </cell-render> | ||
| 33 | + </template> | ||
| 34 | + </el-table-column> | ||
| 48 | </template> | 35 | </template> |
| 49 | </template> | 36 | </template> |
| 50 | - <!-- 已生成列表后的追加列插槽 --> | ||
| 51 | - <slot name="column-append"></slot> | ||
| 52 | - <!-- 末尾列插槽 --> | ||
| 53 | - <slot name="column-end"></slot> | ||
| 54 | - </el-table> | ||
| 55 | - </div> | 37 | + </template> |
| 38 | + <!-- 已生成列表后的追加列插槽 --> | ||
| 39 | + <slot name="column-append"></slot> | ||
| 40 | + <!-- 末尾列插槽 --> | ||
| 41 | + <slot name="column-end"></slot> | ||
| 42 | + </el-table> | ||
| 56 | </template> | 43 | </template> |
| 57 | 44 | ||
| 58 | <script> | 45 | <script> |
| 59 | import { cloneDeep, get, set } from '../form-new/util'; | 46 | import { cloneDeep, get, set } from '../form-new/util'; |
| 60 | -import CellEditable from './cell-editable'; | ||
| 61 | - | ||
| 62 | -const listHasKey = (list, name) => { | ||
| 63 | - let result = false; | ||
| 64 | - for (const row of list) { | ||
| 65 | - if (row[name]) { | ||
| 66 | - result = true; | ||
| 67 | - break; | ||
| 68 | - } | ||
| 69 | - } | ||
| 70 | - return result; | ||
| 71 | -} | ||
| 72 | 47 | ||
| 73 | export default { | 48 | export default { |
| 74 | name: 'TableNew', | 49 | name: 'TableNew', |
| 75 | - components: { CellEditable }, | 50 | + components: { |
| 51 | + CellRender: { | ||
| 52 | + props: { row: Object, item: Object }, | ||
| 53 | + render(h) { | ||
| 54 | + if (this.$scopedSlots.default) { | ||
| 55 | + return h('span', this.$scopedSlots.default()); | ||
| 56 | + } else { | ||
| 57 | + return h('span', get(this.row, this.item.agentKey || this.item.fullKey)); | ||
| 58 | + } | ||
| 59 | + }, | ||
| 60 | + } | ||
| 61 | + }, | ||
| 76 | props: { | 62 | props: { |
| 77 | // 用于实例化本组件绑定v-model的值 | 63 | // 用于实例化本组件绑定v-model的值 |
| 78 | value: Array, | 64 | value: Array, |
| @@ -88,8 +74,6 @@ export default { | @@ -88,8 +74,6 @@ export default { | ||
| 88 | }, | 74 | }, |
| 89 | // 表格事件 | 75 | // 表格事件 |
| 90 | tableEvents: Object, | 76 | tableEvents: Object, |
| 91 | - // 是否可编辑 | ||
| 92 | - editable: Boolean, | ||
| 93 | // 列宽 | 77 | // 列宽 |
| 94 | minWidth: Number, | 78 | minWidth: Number, |
| 95 | }, | 79 | }, |
| @@ -97,9 +81,6 @@ export default { | @@ -97,9 +81,6 @@ export default { | ||
| 97 | return { | 81 | return { |
| 98 | tableList: [], // 表格配置列表 | 82 | tableList: [], // 表格配置列表 |
| 99 | tableData: [], // 表格数据 | 83 | tableData: [], // 表格数据 |
| 100 | - tableRowTemplate: { $editable: true }, // 行数据模板 | ||
| 101 | - tableEditCell: {}, // 正在编辑的单元格 | ||
| 102 | - tableSelection: [], // 表格已选中 | ||
| 103 | }; | 84 | }; |
| 104 | }, | 85 | }, |
| 105 | computed: { | 86 | computed: { |
| @@ -107,25 +88,11 @@ export default { | @@ -107,25 +88,11 @@ export default { | ||
| 107 | instance: { | 88 | instance: { |
| 108 | get() { return this.$refs.table; } | 89 | get() { return this.$refs.table; } |
| 109 | }, | 90 | }, |
| 110 | - // 行编辑状态 | ||
| 111 | - rowEditable() { | ||
| 112 | - return listHasKey(this.tableData, '$editable'); | ||
| 113 | - }, | ||
| 114 | - // 存在新行 | ||
| 115 | - rowNew() { | ||
| 116 | - return listHasKey(this.tableData, '$new'); | ||
| 117 | - }, | ||
| 118 | - // 存在编辑行 | ||
| 119 | - rowEdit() { | ||
| 120 | - return listHasKey(this.tableData, '$edit'); | ||
| 121 | - } | ||
| 122 | }, | 91 | }, |
| 123 | watch: { | 92 | watch: { |
| 124 | value: { | 93 | value: { |
| 125 | handler(val = []) { | 94 | handler(val = []) { |
| 126 | - this.tableData = val.map((o, i) => { | ||
| 127 | - return { ...o, $index: i, $editable: undefined, $new: undefined }; | ||
| 128 | - }); | 95 | + this.tableData = val; |
| 129 | }, | 96 | }, |
| 130 | immediate: true | 97 | immediate: true |
| 131 | }, | 98 | }, |
| @@ -152,7 +119,6 @@ export default { | @@ -152,7 +119,6 @@ export default { | ||
| 152 | generateFullKey(newList); | 119 | generateFullKey(newList); |
| 153 | // 创建输出列表 | 120 | // 创建输出列表 |
| 154 | const result = []; | 121 | const result = []; |
| 155 | - const tableRowTemplate = {}; | ||
| 156 | // 生成列表值的全路径key,即列表项为对象时,对象内的key与上一级的key合并作为全路径key | 122 | // 生成列表值的全路径key,即列表项为对象时,对象内的key与上一级的key合并作为全路径key |
| 157 | const generateFlatList = (list) => { | 123 | const generateFlatList = (list) => { |
| 158 | list.forEach(item => { | 124 | list.forEach(item => { |
| @@ -160,83 +126,17 @@ export default { | @@ -160,83 +126,17 @@ export default { | ||
| 160 | generateFlatList(item.list); | 126 | generateFlatList(item.list); |
| 161 | } else if (!item.group && !item.list) { | 127 | } else if (!item.group && !item.list) { |
| 162 | result.push({ ...item, keyPath: item.fullKey.split('.') }); | 128 | result.push({ ...item, keyPath: item.fullKey.split('.') }); |
| 163 | - set(tableRowTemplate, item.fullKey, undefined); | ||
| 164 | - tableRowTemplate.$editable = true; | ||
| 165 | } | 129 | } |
| 166 | }); | 130 | }); |
| 167 | }; | 131 | }; |
| 168 | generateFlatList(newList); | 132 | generateFlatList(newList); |
| 169 | this.tableList = result; | 133 | this.tableList = result; |
| 170 | - this.tableRowTemplate = tableRowTemplate; | ||
| 171 | }, | 134 | }, |
| 172 | immediate: true | 135 | immediate: true |
| 173 | } | 136 | } |
| 174 | }, | 137 | }, |
| 175 | methods: { | 138 | methods: { |
| 176 | $_get: get, | 139 | $_get: get, |
| 177 | - // 处理新增逻辑 | ||
| 178 | - handleNew() { | ||
| 179 | - const tableData = cloneDeep(this.tableData); | ||
| 180 | - tableData.push({ ...this.tableRowTemplate, $new: true }); | ||
| 181 | - this.tableEditCell = {}; | ||
| 182 | - this.tableData = tableData.map((o, i) => ({ ...o, $index: i })); | ||
| 183 | - }, | ||
| 184 | - // 处理编辑逻辑 | ||
| 185 | - handleEdit() { | ||
| 186 | - const tableData = cloneDeep(this.tableData); | ||
| 187 | - const selectionIndexArr = this.tableSelection.map(i => i.$index); | ||
| 188 | - tableData.forEach((r, i) => { | ||
| 189 | - if (selectionIndexArr.includes(r.$index)) { | ||
| 190 | - tableData[i].$editable = true; | ||
| 191 | - tableData[i].$edit = true; | ||
| 192 | - tableData[i].$new = true; | ||
| 193 | - } | ||
| 194 | - }); | ||
| 195 | - this.tableEditCell = {}; | ||
| 196 | - this.tableData = tableData.map((o, i) => ({ ...o, $index: i })); | ||
| 197 | - }, | ||
| 198 | - // 处理保存逻辑 | ||
| 199 | - handleSave() { | ||
| 200 | - const tableData = cloneDeep(this.tableData); | ||
| 201 | - tableData.forEach((r, i) => { | ||
| 202 | - delete tableData[i].$editable; | ||
| 203 | - }); | ||
| 204 | - this.tableEditCell = {}; | ||
| 205 | - this.tableData = tableData.map((o, i) => ({ ...o, $index: i })); | ||
| 206 | - this.$nextTick(() => { | ||
| 207 | - this.emitTableData(this.tableData); | ||
| 208 | - this.$emit(this.rowEdit ? 'row-edit' : 'row-new', this.tableData.filter(d => d.$new).map(d => { | ||
| 209 | - delete d.$new; | ||
| 210 | - return d; | ||
| 211 | - })); | ||
| 212 | - }); | ||
| 213 | - }, | ||
| 214 | - // 处理取消逻辑 | ||
| 215 | - handleCancel() { | ||
| 216 | - let tableData = cloneDeep(this.tableData); | ||
| 217 | - tableData = tableData.filter(row => !row.$new); | ||
| 218 | - this.emitTableData(tableData); | ||
| 219 | - }, | ||
| 220 | - // 处理删除逻辑 | ||
| 221 | - handleDelete() { | ||
| 222 | - const tableData = cloneDeep(this.tableData); | ||
| 223 | - const selectionIndexArr = this.tableSelection.map(i => i.$index); | ||
| 224 | - if (!this.rowNew && !this.rowEdit) { | ||
| 225 | - this.$emit('row-delete', this.tableSelection); | ||
| 226 | - } | ||
| 227 | - this.tableEditCell = {}; | ||
| 228 | - this.tableData = tableData.filter((d, i) => !selectionIndexArr.includes(i)).map((o, i) => ({ ...o, $index: i })); | ||
| 229 | - }, | ||
| 230 | - // 更新表格数据 | ||
| 231 | - emitTableData(tableData) { | ||
| 232 | - if (this.$listeners['input']) { | ||
| 233 | - this.$emit('input', tableData.map((o, i) => { | ||
| 234 | - return { ...o, $index: i, $editable: undefined, $new: undefined, $edit: undefined }; | ||
| 235 | - })); | ||
| 236 | - } else { | ||
| 237 | - this.tableData = tableData; | ||
| 238 | - } | ||
| 239 | - }, | ||
| 240 | // 绑定表格列显示隐藏状态 | 140 | // 绑定表格列显示隐藏状态 |
| 241 | bindItemVisible(visible = true) { | 141 | bindItemVisible(visible = true) { |
| 242 | let result = visible; | 142 | let result = visible; |
| @@ -249,43 +149,8 @@ export default { | @@ -249,43 +149,8 @@ export default { | ||
| 249 | bindItemRenderChildren(item, scope) { | 149 | bindItemRenderChildren(item, scope) { |
| 250 | return item.render.children instanceof Function ? item.render.children(scope) : this.$_get(scope.row, item.fullKey) | 150 | return item.render.children instanceof Function ? item.render.children(scope) : this.$_get(scope.row, item.fullKey) |
| 251 | }, | 151 | }, |
| 252 | - // 双击单元格 | ||
| 253 | - onCellDblclick(row, column, cell, event) { | ||
| 254 | - if (this.editable && !this.rowNew) { | ||
| 255 | - this.tableEditCell = { index: row.$index, key: column.property }; | ||
| 256 | - } | ||
| 257 | - }, | ||
| 258 | - // 编辑表格更新值 | ||
| 259 | - onCellUpdate({ oldValue, value, row, key, fullKey }) { | ||
| 260 | - this.setCellValue({ value, row, key, fullKey }); | ||
| 261 | - }, | ||
| 262 | - // 编辑表格确认 | ||
| 263 | - onCellUpdateDone({ oldValue, value, row, key, fullKey }) { | ||
| 264 | - this.tableEditCell = {}; | ||
| 265 | - if (this.$listeners['cell-edit']) { | ||
| 266 | - const { tableData } = this.setCellValue({ value, row, key, fullKey }); | ||
| 267 | - this.emitTableData(tableData); | ||
| 268 | - this.$emit('cell-edit', { row, key, fullKey, value }); | ||
| 269 | - } | ||
| 270 | - }, | ||
| 271 | - // 表格取消编辑 | ||
| 272 | - onCellUpdateCancel({ oldValue, value, row, key, fullKey }) { | ||
| 273 | - if (row.$new !== true) { | ||
| 274 | - this.setCellValue({ value: oldValue, row, key, fullKey }); | ||
| 275 | - } | ||
| 276 | - }, | ||
| 277 | - // 设置表格值 | ||
| 278 | - setCellValue({ value, row, key, fullKey }) { | ||
| 279 | - const tableData = cloneDeep(this.tableData); | ||
| 280 | - const tableRow = tableData[row.$index]; | ||
| 281 | - set(tableRow, fullKey, value); | ||
| 282 | - tableData[row.$index] = tableRow; | ||
| 283 | - this.$set(this.tableData, row.$index, tableRow); | ||
| 284 | - return { tableData, tableRow }; | ||
| 285 | - }, | ||
| 286 | // 表格选中 | 152 | // 表格选中 |
| 287 | onSelectionChange(selection) { | 153 | onSelectionChange(selection) { |
| 288 | - this.tableSelection = selection; | ||
| 289 | this.$emit('selection', selection); | 154 | this.$emit('selection', selection); |
| 290 | } | 155 | } |
| 291 | } | 156 | } |