Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Table]editable table support setting colKey to be like a.b.c; support updateEditedCellValue #3137

Merged
merged 7 commits into from
Jul 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions src/hooks/useElementLazyRender.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { onBeforeUnmount, onMounted, Ref, ref, watch } from 'vue';
import observe from '../_common/js/utils/observe';

export function useElementLazyRender(labelRef: Ref<HTMLElement>, lazyLoad: Ref<boolean>) {
const ioObserver = ref<IntersectionObserver>();
const showElement = ref(true);

const handleLazyLoad = () => {
if (!lazyLoad.value) return;
showElement.value = false;
const io = observe(
labelRef.value,
null,
() => {
showElement.value = true;
},
10,
);
ioObserver.value = io;
};

onMounted(handleLazyLoad);

lazyLoad.value && watch([lazyLoad], handleLazyLoad);

onBeforeUnmount(() => {
if (!lazyLoad.value) return;
ioObserver.value?.unobserve(labelRef.value);
});

return {
showElement,
};
}

export default useElementLazyRender;
57 changes: 31 additions & 26 deletions src/table/_example/affix.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<!-- 注意组件父元素的宽度 -->
<div class="tdesign-demo-block-column-large tdesign-demo__table-affix" style="width: 100%">
<div style="width: 830px" class="tdesign-demo-block-column-large tdesign-demo__table-affix">
<div>
<t-checkbox v-model="headerAffixedTop">表头吸顶</t-checkbox>
<t-checkbox v-model="footerAffixedBottom" style="margin-left: 32px">表尾吸底</t-checkbox>
Expand All @@ -16,14 +16,10 @@
:columns="columns"
:foot-data="footData"
:row-class-name="rowClassName"
:pagination="{ defaultCurrent: 1, defaultPageSize: 5, total: TOTAL }"
:header-affixed-top="headerAffixedTop ? { offsetTop: 87, zIndex: 1000 } : undefined"
:footer-affixed-bottom="
footerAffixedBottom ? { offsetBottom: paginationAffixedBottom ? 60 : 0, zIndex: 1000 } : false
"
:horizontal-scroll-affixed-bottom="
horizontalScrollAffixedBottom ? { offsetBottom: paginationAffixedBottom ? 61 : 0, zIndex: 1000 } : false
"
:pagination="pagination"
:header-affixed-top="headerAffixedTop ? headerAffixedTopProps : undefined"
:footer-affixed-bottom="footerAffixedBottom ? footerAffixedBottomProps : false"
:horizontal-scroll-affixed-bottom="horizontalScrollAffixedBottom ? horizontalScrollAffixedBottomProps : false"
:pagination-affixed-bottom="paginationAffixedBottom"
table-layout="fixed"
drag-sort="col"
Expand Down Expand Up @@ -90,9 +86,9 @@ function getColumns(h, { fixedLeftColumn, fixedRightColumn }) {
},
},
{ colKey: 'channel', title: '签署方式', width: '120' },
{ colKey: 'detail.email', title: '邮箱地址', ellipsis: true },
{ colKey: 'matters', title: '申请事项', ellipsis: true },
{ colKey: 'createTime', title: '申请时间' },
{ colKey: 'detail.email', title: '邮箱地址', width: '180', ellipsis: true },
{ colKey: 'matters', title: '申请事项', width: '180', ellipsis: true },
{ colKey: 'createTime', title: '申请时间', width: '120' },
{
colKey: 'operation',
title: '操作',
Expand Down Expand Up @@ -131,6 +127,29 @@ function onDragSortChange({ newData }) {
columns.value = newData;
}

const pagination = ref({ defaultCurrent: 1, defaultPageSize: 5, total: TOTAL });

// 注意保证对象引用不会发生变化,数据的变更始终保持为同一个对象。以免造成表格重新渲染问题
const headerAffixedTopProps = ref({
offsetTop: 87,
zIndex: 1000,
});

const footerAffixedBottomProps = ref({
offsetBottom: paginationAffixedBottom.value ? 60 : 0,
zIndex: 1000,
});

const horizontalScrollAffixedBottomProps = ref({
offsetBottom: paginationAffixedBottom.value ? 61 : 0,
zIndex: 1000,
});

watch([paginationAffixedBottom], (val) => {
footerAffixedBottomProps.value.offsetBottom = val ? 60 : 0;
horizontalScrollAffixedBottomProps.value.offsetBottom = val ? 61 : 0;
});

// 表尾吸顶和底部滚动条,二选一即可,也只能二选一
watch(horizontalScrollAffixedBottom, (val) => {
val && (footerAffixedBottom.value = false);
Expand Down Expand Up @@ -165,17 +184,3 @@ watch(
{ immediate: true },
);
</script>

<style>
/*
* table-layout: auto 模式下,table 元素的宽度设置很重要很重要。
* 如果不设置,列多了之后会挤在一起(如果使用了列宽调整调整功能,切勿设置 min-width 一类模糊宽度)
* **/
.tdesign-demo__table-affix table {
width: 1200px;
}

.tdesign-demo__table-affix .t-table {
max-width: 800px;
}
</style>
3 changes: 2 additions & 1 deletion src/table/_example/custom-col-button.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
<!-- 2. displayColumns 动态设置显示哪些列,受控属性,支持 displayColumns.sync 语法糖 -->
<!-- 3. onDisplayColumnsChange 当前显示列发生变化时触发 -->
<!-- 4. 如果希望顶部内容 和 列配置按钮 保持在同一行,可将内容放在 topContent,并调整按钮父元素宽度(CSS) -->
<!-- 5. resizable and tableLayout: fixed is suggested -->
<!-- 受控用法,示例代码有效,勿删 -->
<t-table
v-model:displayColumns="displayColumns"
Expand All @@ -32,8 +33,8 @@
}"
:pagination="{ defaultPageSize: 5, defaultCurrent: 1, total: 100 }"
:bordered="bordered"
table-layout="auto"
stripe
resizable
@column-change="onColumnChange"
>
</t-table>
Expand Down
14 changes: 10 additions & 4 deletions src/table/_example/editable-row.vue
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ import dayjs from 'dayjs';
const initData = new Array(5).fill(null).map((_, i) => ({
key: String(i + 1),
firstName: ['贾明', '张三', '王芳'][i % 3],
user: {
firstName: ['贾明', '张三', '王芳'][i % 3],
},
status: i % 3,
email: [
'espinke0@apache.org',
Expand Down Expand Up @@ -138,7 +141,7 @@ const STATUS_OPTIONS = [
const columns = computed(() => [
{
title: '申请人',
colKey: 'firstName',
colKey: 'user.firstName',
align: align.value,
width: 120,
// 编辑状态相关配置,全部集中在 edit
Expand Down Expand Up @@ -183,10 +186,13 @@ const columns = computed(() => [
cell: (h, { row }) => row.letters.join('、'),
edit: {
component: Select,
// props, 透传全部属性到 Select 组件
// props 为函数时,参数有:col, row, rowIndex, colIndex, editedRow。一般用于实现编辑组件之间的联动
/**
* 1. pass props to Select
* 2. props 为函数时,参数有:col, row, rowIndex, colIndex, editedRow,updateEditedCellValue。一般用于实现编辑组件之间的联动
* 3. updateEditedCellValue used to update value of editable cell
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
props: ({ col, row, rowIndex, colIndex, editedRow }) => {
props: ({ col, row, rowIndex, colIndex, editedRow, updateEditedCellValue }) => {
return {
multiple: true,
minCollapsedNum: 1,
Expand Down
6 changes: 4 additions & 2 deletions src/table/base-table-props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ export default {
lastFullRow: {
type: [String, Function] as PropType<TdBaseTableProps['lastFullRow']>,
},
/** 是否启用整个表格元素的懒加载,当页面滚动到可视区域后再渲染表格。注意和表格内部行滚动懒加载的区别,内部行滚动无论表格是否在可视区域都会默认渲染第一屏的行元素 */
lazyLoad: Boolean,
/** 加载中状态。值为 `true` 会显示默认加载中样式,可以通过 Function 和 插槽 自定义加载状态呈现内容和样式。值为 `false` 则会取消加载状态 */
loading: {
type: [Boolean, Function] as PropType<TdBaseTableProps['loading']>,
Expand All @@ -114,7 +116,7 @@ export default {
paginationAffixedBottom: {
type: [Boolean, Object] as PropType<TdBaseTableProps['paginationAffixedBottom']>,
},
/** 是否允许调整列宽。如果想要配置宽度可调整的最小值和最大值,请使用 `column.resize`,示例:`columns: [{ resize: { minWidth: 120, maxWidth: 300 } }]`。<br/> 默认规则:因列宽超出存在横向滚动条时,列宽调整仅影响当前列宽和总列宽;表格列较少没有横向滚动条时,列宽调整表现为自身宽度和相邻宽度变化 */
/** 是否允许调整列宽,设置 `tableLayout=fixed` 效果更友好,此时不允许通过 CSS 设置 `table`元素宽度,也不允许设置 `tableContentWidth`。一般不建议在列宽调整场景使用 `tableLayout: auto`。如果想要配置宽度可调整的最小值和最大值,请使用 `column.resize`,示例:`columns: [{ resize: { minWidth: 120, maxWidth: 300 } }]`。<br/> 默认规则:因列宽超出存在横向滚动条时,列宽调整仅影响当前列宽和总列宽;表格列较少没有横向滚动条时,列宽调整表现为自身宽度和相邻宽度变化 */
resizable: Boolean,
/** HTML 标签 `tr` 的属性。类型为 Function 时,参数说明:`params.row` 表示行数据;`params.rowIndex` 表示行下标;`params.type=body` 表示属性作用于 `tbody` 中的元素;`params.type=foot` 表示属性作用于 `tfoot` 中的元素。<br />示例一:{ draggable: true },<br />示例二:[{ draggable: true }, { title: '超出省略显示' }]。<br /> 示例三:() => [{ draggable: true }] */
rowAttributes: {
Expand Down Expand Up @@ -163,7 +165,7 @@ export default {
type: String,
default: '',
},
/** 表格布局方式 */
/** 表格布局方式,`<table>` 元素原生属性。[MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/table-layout)。注意,在列宽调整下场景只能使用 `fixed` 模式 */
tableLayout: {
type: String as PropType<TdBaseTableProps['tableLayout']>,
default: 'fixed' as TdBaseTableProps['tableLayout'],
Expand Down
21 changes: 12 additions & 9 deletions src/table/base-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { getAffixProps } from './utils';
import { Styles } from '../common';
import { getIEVersion } from '../_common/js/utils/helper';
import { BaseTableInstanceFunctions } from './type';
import log from '../_common/js/log';

export const BASE_TABLE_EVENTS = ['page-change', 'cell-click', 'scroll', 'scrollX', 'scrollY'];
export const BASE_TABLE_ALL_EVENTS = ROW_LISTENERS.map((t) => `row-${t}`).concat(BASE_TABLE_EVENTS);
Expand Down Expand Up @@ -121,7 +122,7 @@ export default defineComponent({
updateTableAfterColumnResize,
onColumnResizeChange: props.onColumnResizeChange,
});
const { resizeLineRef, resizeLineStyle, setEffectColMap, updateTableWidthOnColumnChange } = columnResizeParams;
const { resizeLineRef, resizeLineStyle, setEffectColMap } = columnResizeParams;

const dynamicBaseTableClasses = computed(() => [
tableClasses.value,
Expand Down Expand Up @@ -284,7 +285,6 @@ export default defineComponent({
tableBodyRef,
virtualConfig,
showAffixPagination,
updateTableWidthOnColumnChange,
scrollToElement: virtualConfig.scrollToElement,
renderPagination,
renderTNode,
Expand All @@ -302,13 +302,19 @@ export default defineComponent({
},

render() {
const { rowAndColFixedPosition } = this;
const { rowAndColFixedPosition, tableLayout } = this;
const data = this.isPaginateData ? this.dataSource : this.data;
const columns = this.spansAndLeafNodes?.leafColumns || this.columns;

const columnResizable = computed(() => props.allowResizeColumnWidth ?? props.resizable);
const columnResizable = this.allowResizeColumnWidth ?? this.resizable;
if (columnResizable && tableLayout === 'auto') {
log.warn(
'Table',
'table-layout can not be `auto`, cause you are using column resizable, set `table-layout: fixed` please.',
);
}

const defaultColWidth = this.tableLayout === 'fixed' && this.isWidthOverflow ? '100px' : undefined;
const defaultColWidth = tableLayout === 'fixed' && this.isWidthOverflow ? '100px' : undefined;

const renderColGroup = (isAffixHeader = true) => (
<colgroup>
Expand Down Expand Up @@ -535,10 +541,7 @@ export default defineComponent({
>
{renderColGroup(false)}
{this.showHeader && (
<THead
v-slots={this.$slots}
{...{ ...headProps, thWidthList: columnResizable.value ? this.thWidthList : {} }}
/>
<THead v-slots={this.$slots} {...{ ...headProps, thWidthList: columnResizable ? this.thWidthList : {} }} />
)}
<TBody v-slots={this.$slots} {...tableBodyProps} />
<TFoot
Expand Down
20 changes: 17 additions & 3 deletions src/table/editable-cell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { computed, defineComponent, onMounted, PropType, ref, SetupContext, toRe
import get from 'lodash/get';
import set from 'lodash/set';
import isFunction from 'lodash/isFunction';
import cloneDeep from 'lodash/cloneDeep';
import { Edit1Icon as TdEdit1Icon } from 'tdesign-icons-vue-next';

import {
TableRowData,
PrimaryTableCol,
Expand Down Expand Up @@ -85,8 +85,13 @@ export default defineComponent({
const classPrefix = usePrefixClass();

const { Edit1Icon } = useGlobalIcon({ Edit1Icon: TdEdit1Icon });

const updateEditedCellValue = (val: any) => {
editValue.value = val;
};

const editOnListeners = computed(() => {
return col.value.edit?.on?.({ ...cellParams.value, editedRow: currentRow.value }) || {};
return col.value.edit?.on?.({ ...cellParams.value, editedRow: currentRow.value, updateEditedCellValue }) || {};
});

const cellParams = computed(() => ({
Expand All @@ -97,8 +102,16 @@ export default defineComponent({
}));

const currentRow = computed(() => {
const { colKey } = col.value;
// handle colKey like a.b.c
const [firstKey, ...restKeys] = colKey.split('.') || [];
const newRow = { ...row.value };
set(newRow, col.value.colKey, editValue.value);
if (restKeys.length) {
newRow[firstKey] = cloneDeep(row.value[firstKey]);
set(newRow[firstKey], restKeys.join('.'), editValue.value);
} else {
set(newRow, colKey, editValue.value);
}
return newRow;
});

Expand All @@ -123,6 +136,7 @@ export default defineComponent({
? edit.props({
...cellParams.value,
editedRow: currentRow.value,
updateEditedCellValue,
})
: { ...edit.props };
// to remove warn: runtime-core.esm-bundler.js:38 [Vue warn]: Invalid prop: type check failed for prop "onChange". Expected Function, got Array
Expand Down
15 changes: 2 additions & 13 deletions src/table/hooks/useColumnController.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import { computed, ref, SetupContext, toRefs, watch } from 'vue';
import { SettingIcon as TdSettingIcon } from 'tdesign-icons-vue-next';
import intersection from 'lodash/intersection';
import xorWith from 'lodash/xorWith';
import Checkbox, {
CheckboxGroup,
CheckboxGroupValue,
Expand Down Expand Up @@ -32,13 +31,7 @@ export function getColumnKeys(columns: PrimaryTableCol[], keys = new Set<string>
return keys;
}

export default function useColumnController(
props: TdPrimaryTableProps,
context: SetupContext,
extra?: {
onColumnReduce: (reduceKeys: CheckboxGroupValue) => void;
},
) {
export default function useColumnController(props: TdPrimaryTableProps, context: SetupContext) {
const { classPrefix, globalConfig } = useConfig('table');
const { SettingIcon } = useGlobalIcon({ SettingIcon: TdSettingIcon });
const { columns, columnController, displayColumns, columnControllerVisible } = toRefs(props);
Expand All @@ -65,12 +58,8 @@ export default function useColumnController(

const intersectionChecked = computed(() => intersection(columnCheckboxKeys.value, [...enabledColKeys.value]));

watch([displayColumns], ([val], [oldVal]) => {
watch([displayColumns], ([val]) => {
columnCheckboxKeys.value = val || props.defaultDisplayColumns || keys;
if (val.length < oldVal.length) {
const reduceKeys = xorWith(oldVal, val);
extra?.onColumnReduce?.(reduceKeys);
}
});

function getCheckboxOptions(columns: PrimaryTableCol[], arr: CheckboxOptionObj[] = []) {
Expand Down
15 changes: 0 additions & 15 deletions src/table/hooks/useColumnResize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -330,26 +330,11 @@ export default function useColumnResize(params: {
document.ondragstart = () => false;
};

/**
* 对外暴露函数:更新列数量减少时的表格宽度
* @params colKeys 减少的列
*/
const updateTableWidthOnColumnChange = (colKeys: string[]) => {
const thWidthList = getThWidthList('calculate');
let reduceWidth = 0;
colKeys.forEach((key) => {
reduceWidth += thWidthList[key];
});
const oldTotalWidth = Object.values(thWidthList).reduce((r = 0, n) => r + n);
setTableElmWidth(oldTotalWidth - reduceWidth);
};

return {
resizeLineRef,
resizeLineStyle,
onColumnMouseover,
onColumnMousedown,
setEffectColMap,
updateTableWidthOnColumnChange,
};
}
2 changes: 1 addition & 1 deletion src/table/hooks/useEditableRow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export default function useRowEdit(props: PrimaryTableProps) {
resolve({ ...item, errorList: [] });
return;
}
validate(editedRow[col.colKey], rules).then((r) => {
validate(get(editedRow, col.colKey), rules).then((r) => {
resolve({ ...item, errorList: r.filter((t) => !t.result) });
});
}),
Expand Down
Loading
Loading