Commit b63f7d17dee2c0332e753ee445d61db63bd28236
Committed by
GitHub
1 parent
67d514ad
feat: 增强可编辑单元格功能 (#1576)
1. 增加可编辑单元格非编辑状态下可自定义样式 2. 扩展editComponentProps,可接受方法
Showing
3 changed files
with
100 additions
and
54 deletions
src/components/Table/src/components/editable/EditableCell.vue
1 | -<template> | ||
2 | - <div :class="prefixCls"> | ||
3 | - <div | ||
4 | - v-show="!isEdit" | ||
5 | - :class="{ [`${prefixCls}__normal`]: true, 'ellipsis-cell': column.ellipsis }" | ||
6 | - @click="handleEdit" | ||
7 | - > | ||
8 | - <div class="cell-content" :title="column.ellipsis ? getValues ?? '' : ''"> | ||
9 | - {{ getValues || getValues === 0 ? getValues : ' ' }} | ||
10 | - </div> | ||
11 | - <FormOutlined :class="`${prefixCls}__normal-icon`" v-if="!column.editRow" /> | ||
12 | - </div> | ||
13 | - | ||
14 | - <a-spin v-if="isEdit" :spinning="spinning"> | ||
15 | - <div :class="`${prefixCls}__wrapper`" v-click-outside="onClickOutside"> | ||
16 | - <CellComponent | ||
17 | - v-bind="getComponentProps" | ||
18 | - :component="getComponent" | ||
19 | - :style="getWrapperStyle" | ||
20 | - :popoverVisible="getRuleVisible" | ||
21 | - :rule="getRule" | ||
22 | - :ruleMessage="ruleMessage" | ||
23 | - :class="getWrapperClass" | ||
24 | - ref="elRef" | ||
25 | - @change="handleChange" | ||
26 | - @options-change="handleOptionsChange" | ||
27 | - @pressEnter="handleEnter" | ||
28 | - /> | ||
29 | - <div :class="`${prefixCls}__action`" v-if="!getRowEditable"> | ||
30 | - <CheckOutlined :class="[`${prefixCls}__icon`, 'mx-2']" @click="handleSubmitClick" /> | ||
31 | - <CloseOutlined :class="`${prefixCls}__icon `" @click="handleCancel" /> | ||
32 | - </div> | ||
33 | - </div> | ||
34 | - </a-spin> | ||
35 | - </div> | ||
36 | -</template> | ||
37 | -<script lang="ts"> | 1 | +<script lang="tsx"> |
38 | import type { CSSProperties, PropType } from 'vue'; | 2 | import type { CSSProperties, PropType } from 'vue'; |
39 | import { computed, defineComponent, nextTick, ref, toRaw, unref, watchEffect } from 'vue'; | 3 | import { computed, defineComponent, nextTick, ref, toRaw, unref, watchEffect } from 'vue'; |
40 | import type { BasicColumn } from '../../types/table'; | 4 | import type { BasicColumn } from '../../types/table'; |
@@ -56,7 +20,7 @@ | @@ -56,7 +20,7 @@ | ||
56 | 20 | ||
57 | export default defineComponent({ | 21 | export default defineComponent({ |
58 | name: 'EditableCell', | 22 | name: 'EditableCell', |
59 | - components: { FormOutlined, CloseOutlined, CheckOutlined, CellComponent, ASpin: Spin }, | 23 | + components: { FormOutlined, CloseOutlined, CheckOutlined, CellComponent, Spin }, |
60 | directives: { | 24 | directives: { |
61 | clickOutside, | 25 | clickOutside, |
62 | }, | 26 | }, |
@@ -100,13 +64,6 @@ | @@ -100,13 +64,6 @@ | ||
100 | }); | 64 | }); |
101 | 65 | ||
102 | const getComponentProps = computed(() => { | 66 | const getComponentProps = computed(() => { |
103 | - const compProps = props.column?.editComponentProps ?? {}; | ||
104 | - const component = unref(getComponent); | ||
105 | - const apiSelectProps: Recordable = {}; | ||
106 | - if (component === 'ApiSelect') { | ||
107 | - apiSelectProps.cache = true; | ||
108 | - } | ||
109 | - | ||
110 | const isCheckValue = unref(getIsCheckComp); | 67 | const isCheckValue = unref(getIsCheckComp); |
111 | 68 | ||
112 | const valueField = isCheckValue ? 'checked' : 'value'; | 69 | const valueField = isCheckValue ? 'checked' : 'value'; |
@@ -114,19 +71,30 @@ | @@ -114,19 +71,30 @@ | ||
114 | 71 | ||
115 | const value = isCheckValue ? (isNumber(val) && isBoolean(val) ? val : !!val) : val; | 72 | const value = isCheckValue ? (isNumber(val) && isBoolean(val) ? val : !!val) : val; |
116 | 73 | ||
74 | + let compProps = props.column?.editComponentProps ?? {}; | ||
75 | + const { record, column, index } = props; | ||
76 | + | ||
77 | + if (isFunction(compProps)) { | ||
78 | + compProps = compProps({ text: val, record, column, index }) ?? {}; | ||
79 | + } | ||
80 | + const component = unref(getComponent); | ||
81 | + const apiSelectProps: Recordable = {}; | ||
82 | + if (component === 'ApiSelect') { | ||
83 | + apiSelectProps.cache = true; | ||
84 | + } | ||
85 | + | ||
117 | return { | 86 | return { |
118 | size: 'small', | 87 | size: 'small', |
119 | getPopupContainer: () => unref(table?.wrapRef.value) ?? document.body, | 88 | getPopupContainer: () => unref(table?.wrapRef.value) ?? document.body, |
120 | - getCalendarContainer: () => unref(table?.wrapRef.value) ?? document.body, | ||
121 | placeholder: createPlaceholderMessage(unref(getComponent)), | 89 | placeholder: createPlaceholderMessage(unref(getComponent)), |
122 | ...apiSelectProps, | 90 | ...apiSelectProps, |
123 | ...omit(compProps, 'onChange'), | 91 | ...omit(compProps, 'onChange'), |
124 | [valueField]: value, | 92 | [valueField]: value, |
125 | - }; | 93 | + } as any; |
126 | }); | 94 | }); |
127 | 95 | ||
128 | const getValues = computed(() => { | 96 | const getValues = computed(() => { |
129 | - const { editComponentProps, editValueMap } = props.column; | 97 | + const { editValueMap } = props.column; |
130 | 98 | ||
131 | const value = unref(currentValueRef); | 99 | const value = unref(currentValueRef); |
132 | 100 | ||
@@ -139,7 +107,8 @@ | @@ -139,7 +107,8 @@ | ||
139 | return value; | 107 | return value; |
140 | } | 108 | } |
141 | 109 | ||
142 | - const options: LabelValueOptions = editComponentProps?.options ?? (unref(optionsRef) || []); | 110 | + const options: LabelValueOptions = |
111 | + unref(getComponentProps)?.options ?? (unref(optionsRef) || []); | ||
143 | const option = options.find((item) => `${item.value}` === `${value}`); | 112 | const option = options.find((item) => `${item.value}` === `${value}`); |
144 | 113 | ||
145 | return option?.label ?? value; | 114 | return option?.label ?? value; |
@@ -197,7 +166,7 @@ | @@ -197,7 +166,7 @@ | ||
197 | } else if (isString(e) || isBoolean(e) || isNumber(e)) { | 166 | } else if (isString(e) || isBoolean(e) || isNumber(e)) { |
198 | currentValueRef.value = e; | 167 | currentValueRef.value = e; |
199 | } | 168 | } |
200 | - const onChange = props.column?.editComponentProps?.onChange; | 169 | + const onChange = unref(getComponentProps)?.onChange; |
201 | if (onChange && isFunction(onChange)) onChange(...arguments); | 170 | if (onChange && isFunction(onChange)) onChange(...arguments); |
202 | 171 | ||
203 | table.emit?.('edit-change', { | 172 | table.emit?.('edit-change', { |
@@ -322,7 +291,7 @@ | @@ -322,7 +291,7 @@ | ||
322 | 291 | ||
323 | // only ApiSelect or TreeSelect | 292 | // only ApiSelect or TreeSelect |
324 | function handleOptionsChange(options: LabelValueOptions) { | 293 | function handleOptionsChange(options: LabelValueOptions) { |
325 | - const { replaceFields } = props.column?.editComponentProps ?? {}; | 294 | + const { replaceFields } = unref(getComponentProps); |
326 | const component = unref(getComponent); | 295 | const component = unref(getComponent); |
327 | if (component === 'ApiTreeSelect') { | 296 | if (component === 'ApiTreeSelect') { |
328 | const { title = 'title', value = 'value', children = 'children' } = replaceFields || {}; | 297 | const { title = 'title', value = 'value', children = 'children' } = replaceFields || {}; |
@@ -355,7 +324,7 @@ | @@ -355,7 +324,7 @@ | ||
355 | 324 | ||
356 | if (props.column.dataIndex) { | 325 | if (props.column.dataIndex) { |
357 | if (!props.record.editValueRefs) props.record.editValueRefs = {}; | 326 | if (!props.record.editValueRefs) props.record.editValueRefs = {}; |
358 | - props.record.editValueRefs[props.column.dataIndex] = currentValueRef; | 327 | + props.record.editValueRefs[props.column.dataIndex as any] = currentValueRef; |
359 | } | 328 | } |
360 | /* eslint-disable */ | 329 | /* eslint-disable */ |
361 | props.record.onCancelEdit = () => { | 330 | props.record.onCancelEdit = () => { |
@@ -398,6 +367,59 @@ | @@ -398,6 +367,59 @@ | ||
398 | spinning, | 367 | spinning, |
399 | }; | 368 | }; |
400 | }, | 369 | }, |
370 | + render() { | ||
371 | + return ( | ||
372 | + <div class={this.prefixCls}> | ||
373 | + <div | ||
374 | + v-show={!this.isEdit} | ||
375 | + class={{ [`${this.prefixCls}__normal`]: true, 'ellipsis-cell': this.column.ellipsis }} | ||
376 | + onClick={this.handleEdit} | ||
377 | + > | ||
378 | + <div class="cell-content" title={this.column.ellipsis ? this.getValues ?? '' : ''}> | ||
379 | + {this.column.editRender | ||
380 | + ? this.column.editRender({ | ||
381 | + text: this.value, | ||
382 | + record: this.record as Recordable, | ||
383 | + column: this.column, | ||
384 | + index: this.index, | ||
385 | + }) | ||
386 | + : this.getValues | ||
387 | + ? this.getValues | ||
388 | + : '\u00A0'} | ||
389 | + </div> | ||
390 | + {!this.column.editRow && <FormOutlined class={`${this.prefixCls}__normal-icon`} />} | ||
391 | + </div> | ||
392 | + {this.isEdit && ( | ||
393 | + <Spin spinning={this.spinning}> | ||
394 | + <div class={`${this.prefixCls}__wrapper`} v-click-outside={this.onClickOutside}> | ||
395 | + <CellComponent | ||
396 | + {...this.getComponentProps} | ||
397 | + component={this.getComponent} | ||
398 | + style={this.getWrapperStyle} | ||
399 | + popoverVisible={this.getRuleVisible} | ||
400 | + rule={this.getRule} | ||
401 | + ruleMessage={this.ruleMessage} | ||
402 | + class={this.getWrapperClass} | ||
403 | + ref="elRef" | ||
404 | + onChange={this.handleChange} | ||
405 | + onOptionsChange={this.handleOptionsChange} | ||
406 | + onPressEnter={this.handleEnter} | ||
407 | + /> | ||
408 | + {!this.getRowEditable && ( | ||
409 | + <div class={`${this.prefixCls}__action`}> | ||
410 | + <CheckOutlined | ||
411 | + class={[`${this.prefixCls}__icon`, 'mx-2']} | ||
412 | + onClick={this.handleSubmitClick} | ||
413 | + /> | ||
414 | + <CloseOutlined class={`${this.prefixCls}__icon `} onClick={this.handleCancel} /> | ||
415 | + </div> | ||
416 | + )} | ||
417 | + </div> | ||
418 | + </Spin> | ||
419 | + )} | ||
420 | + </div> | ||
421 | + ); | ||
422 | + }, | ||
401 | }); | 423 | }); |
402 | </script> | 424 | </script> |
403 | <style lang="less"> | 425 | <style lang="less"> |
src/components/Table/src/types/table.ts
@@ -441,7 +441,14 @@ export interface BasicColumn extends ColumnProps { | @@ -441,7 +441,14 @@ export interface BasicColumn extends ColumnProps { | ||
441 | editRow?: boolean; | 441 | editRow?: boolean; |
442 | editable?: boolean; | 442 | editable?: boolean; |
443 | editComponent?: ComponentType; | 443 | editComponent?: ComponentType; |
444 | - editComponentProps?: Recordable; | 444 | + editComponentProps?: |
445 | + | ((opt: { | ||
446 | + text: string | number | boolean | Recordable; | ||
447 | + record: Recordable; | ||
448 | + column: BasicColumn; | ||
449 | + index: number; | ||
450 | + }) => Recordable) | ||
451 | + | Recordable; | ||
445 | editRule?: boolean | ((text: string, record: Recordable) => Promise<string>); | 452 | editRule?: boolean | ((text: string, record: Recordable) => Promise<string>); |
446 | editValueMap?: (value: any) => string; | 453 | editValueMap?: (value: any) => string; |
447 | onEditRow?: () => void; | 454 | onEditRow?: () => void; |
@@ -449,6 +456,13 @@ export interface BasicColumn extends ColumnProps { | @@ -449,6 +456,13 @@ export interface BasicColumn extends ColumnProps { | ||
449 | auth?: RoleEnum | RoleEnum[] | string | string[]; | 456 | auth?: RoleEnum | RoleEnum[] | string | string[]; |
450 | // 业务控制是否显示 | 457 | // 业务控制是否显示 |
451 | ifShow?: boolean | ((column: BasicColumn) => boolean); | 458 | ifShow?: boolean | ((column: BasicColumn) => boolean); |
459 | + // 自定义修改后显示的内容 | ||
460 | + editRender?: (opt: { | ||
461 | + text: string | number | boolean | Recordable; | ||
462 | + record: Recordable; | ||
463 | + column: BasicColumn; | ||
464 | + index: number; | ||
465 | + }) => VNodeChild | JSX.Element; | ||
452 | } | 466 | } |
453 | 467 | ||
454 | export type ColumnChangeParam = { | 468 | export type ColumnChangeParam = { |
src/views/demo/table/EditCellTable.vue
@@ -9,13 +9,14 @@ | @@ -9,13 +9,14 @@ | ||
9 | </div> | 9 | </div> |
10 | </template> | 10 | </template> |
11 | <script lang="ts"> | 11 | <script lang="ts"> |
12 | - import { defineComponent } from 'vue'; | 12 | + import { defineComponent, h } from 'vue'; |
13 | import { BasicTable, useTable, BasicColumn } from '/@/components/Table'; | 13 | import { BasicTable, useTable, BasicColumn } from '/@/components/Table'; |
14 | import { optionsListApi } from '/@/api/demo/select'; | 14 | import { optionsListApi } from '/@/api/demo/select'; |
15 | 15 | ||
16 | import { demoListApi } from '/@/api/demo/table'; | 16 | import { demoListApi } from '/@/api/demo/table'; |
17 | import { treeOptionsListApi } from '/@/api/demo/tree'; | 17 | import { treeOptionsListApi } from '/@/api/demo/tree'; |
18 | import { useMessage } from '/@/hooks/web/useMessage'; | 18 | import { useMessage } from '/@/hooks/web/useMessage'; |
19 | + import { Progress } from 'ant-design-vue'; | ||
19 | const columns: BasicColumn[] = [ | 20 | const columns: BasicColumn[] = [ |
20 | { | 21 | { |
21 | title: '输入框', | 22 | title: '输入框', |
@@ -60,6 +61,15 @@ | @@ -60,6 +61,15 @@ | ||
60 | editRule: true, | 61 | editRule: true, |
61 | editComponent: 'InputNumber', | 62 | editComponent: 'InputNumber', |
62 | width: 200, | 63 | width: 200, |
64 | + editComponentProps: () => { | ||
65 | + return { | ||
66 | + max: 100, | ||
67 | + min: 0, | ||
68 | + }; | ||
69 | + }, | ||
70 | + editRender: ({ text }) => { | ||
71 | + return h(Progress, { percent: Number(text) }); | ||
72 | + }, | ||
63 | }, | 73 | }, |
64 | { | 74 | { |
65 | title: '下拉框', | 75 | title: '下拉框', |