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 2 import type { CSSProperties, PropType } from 'vue';
39 3 import { computed, defineComponent, nextTick, ref, toRaw, unref, watchEffect } from 'vue';
40 4 import type { BasicColumn } from '../../types/table';
... ... @@ -56,7 +20,7 @@
56 20  
57 21 export default defineComponent({
58 22 name: 'EditableCell',
59   - components: { FormOutlined, CloseOutlined, CheckOutlined, CellComponent, ASpin: Spin },
  23 + components: { FormOutlined, CloseOutlined, CheckOutlined, CellComponent, Spin },
60 24 directives: {
61 25 clickOutside,
62 26 },
... ... @@ -100,13 +64,6 @@
100 64 });
101 65  
102 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 67 const isCheckValue = unref(getIsCheckComp);
111 68  
112 69 const valueField = isCheckValue ? 'checked' : 'value';
... ... @@ -114,19 +71,30 @@
114 71  
115 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 86 return {
118 87 size: 'small',
119 88 getPopupContainer: () => unref(table?.wrapRef.value) ?? document.body,
120   - getCalendarContainer: () => unref(table?.wrapRef.value) ?? document.body,
121 89 placeholder: createPlaceholderMessage(unref(getComponent)),
122 90 ...apiSelectProps,
123 91 ...omit(compProps, 'onChange'),
124 92 [valueField]: value,
125   - };
  93 + } as any;
126 94 });
127 95  
128 96 const getValues = computed(() => {
129   - const { editComponentProps, editValueMap } = props.column;
  97 + const { editValueMap } = props.column;
130 98  
131 99 const value = unref(currentValueRef);
132 100  
... ... @@ -139,7 +107,8 @@
139 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 112 const option = options.find((item) => `${item.value}` === `${value}`);
144 113  
145 114 return option?.label ?? value;
... ... @@ -197,7 +166,7 @@
197 166 } else if (isString(e) || isBoolean(e) || isNumber(e)) {
198 167 currentValueRef.value = e;
199 168 }
200   - const onChange = props.column?.editComponentProps?.onChange;
  169 + const onChange = unref(getComponentProps)?.onChange;
201 170 if (onChange && isFunction(onChange)) onChange(...arguments);
202 171  
203 172 table.emit?.('edit-change', {
... ... @@ -322,7 +291,7 @@
322 291  
323 292 // only ApiSelect or TreeSelect
324 293 function handleOptionsChange(options: LabelValueOptions) {
325   - const { replaceFields } = props.column?.editComponentProps ?? {};
  294 + const { replaceFields } = unref(getComponentProps);
326 295 const component = unref(getComponent);
327 296 if (component === 'ApiTreeSelect') {
328 297 const { title = 'title', value = 'value', children = 'children' } = replaceFields || {};
... ... @@ -355,7 +324,7 @@
355 324  
356 325 if (props.column.dataIndex) {
357 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 329 /* eslint-disable */
361 330 props.record.onCancelEdit = () => {
... ... @@ -398,6 +367,59 @@
398 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 424 </script>
403 425 <style lang="less">
... ...
src/components/Table/src/types/table.ts
... ... @@ -441,7 +441,14 @@ export interface BasicColumn extends ColumnProps {
441 441 editRow?: boolean;
442 442 editable?: boolean;
443 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 452 editRule?: boolean | ((text: string, record: Recordable) => Promise<string>);
446 453 editValueMap?: (value: any) => string;
447 454 onEditRow?: () => void;
... ... @@ -449,6 +456,13 @@ export interface BasicColumn extends ColumnProps {
449 456 auth?: RoleEnum | RoleEnum[] | string | string[];
450 457 // 业务控制是否显示
451 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 468 export type ColumnChangeParam = {
... ...
src/views/demo/table/EditCellTable.vue
... ... @@ -9,13 +9,14 @@
9 9 </div>
10 10 </template>
11 11 <script lang="ts">
12   - import { defineComponent } from 'vue';
  12 + import { defineComponent, h } from 'vue';
13 13 import { BasicTable, useTable, BasicColumn } from '/@/components/Table';
14 14 import { optionsListApi } from '/@/api/demo/select';
15 15  
16 16 import { demoListApi } from '/@/api/demo/table';
17 17 import { treeOptionsListApi } from '/@/api/demo/tree';
18 18 import { useMessage } from '/@/hooks/web/useMessage';
  19 + import { Progress } from 'ant-design-vue';
19 20 const columns: BasicColumn[] = [
20 21 {
21 22 title: '输入框',
... ... @@ -60,6 +61,15 @@
60 61 editRule: true,
61 62 editComponent: 'InputNumber',
62 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 75 title: '下拉框',
... ...