Commit b63f7d17dee2c0332e753ee445d61db63bd28236

Authored by lzdjack
Committed by GitHub
1 parent 67d514ad

feat: 增强可编辑单元格功能 (#1576)

1. 增加可编辑单元格非编辑状态下可自定义样式
2. 扩展editComponentProps,可接受方法
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 : '&nbsp;' }}  
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: '下拉框',