Skip to content

Commit

Permalink
[Table]合并单元格支持动态数据 (#969)
Browse files Browse the repository at this point in the history
* feat(table): some features

* docs(table): update comments

* feat(table): code improve

* test(unit): update snapshots

* fix(table): dynamic data fro rowspan and colspan

* style(table): remove unused variable
  • Loading branch information
chaishi authored Jun 10, 2022
1 parent b10ca39 commit 0c02760
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 36 deletions.
74 changes: 74 additions & 0 deletions src/table/hooks/useRowspanAndColspan.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { ref, watch, Ref } from 'vue';
import { BaseTableCellParams, BaseTableCol, TableRowData, TableRowspanAndColspanFunc } from '../type';

export interface SkipSpansValue {
colspan?: number;
rowspan?: number;
skipped?: boolean;
}

export default function useRowspanAndColspan(
data: Ref<TableRowData[]>,
columns: Ref<BaseTableCol<TableRowData>[]>,
rowspanAndColspan: TableRowspanAndColspanFunc<TableRowData>,
) {
const skipSpansMap = ref(new Map<string, SkipSpansValue>());

// 计算单元格是否跳过渲染
const onTrRowspanOrColspan = (params: BaseTableCellParams<TableRowData>, skipSpansValue: SkipSpansValue) => {
const { rowIndex, colIndex } = params;
if (!skipSpansValue.rowspan && !skipSpansValue.colspan) return;
const maxRowIndex = rowIndex + (skipSpansValue.rowspan || 1);
const maxColIndex = colIndex + (skipSpansValue.colspan || 1);
for (let i = rowIndex; i < maxRowIndex; i++) {
for (let j = colIndex; j < maxColIndex; j++) {
if (i !== rowIndex || j !== colIndex) {
const cellKey = [i, j].join();
const state = skipSpansMap.value.get(cellKey) || {};
state.skipped = true;
skipSpansMap.value.set(cellKey, state);
}
}
}
};

// 计算单元格是否需要设置 rowspan 和 colspan
const updateSkipSpansMap = (
data: TableRowData[],
columns: BaseTableCol<TableRowData>[],
rowspanAndColspan: TableRowspanAndColspanFunc<TableRowData>,
) => {
if (!data || !rowspanAndColspan) return;
for (let i = 0, len = data.length; i < len; i++) {
const row = data[i];
for (let j = 0, colLen = columns.length; j < colLen; j++) {
const params = {
row,
col: columns[j],
rowIndex: i,
colIndex: j,
};
const cellKey = [i, j].join();
const state = skipSpansMap.value.get(cellKey) || {};
const o = rowspanAndColspan(params) || {};
if (o.rowspan > 1 || o.colspan > 1 || state.rowspan || state.colspan) {
o.rowspan > 1 && (state.rowspan = o.rowspan);
o.colspan > 1 && (state.colspan = o.colspan);
skipSpansMap.value.set(cellKey, state);
}
onTrRowspanOrColspan?.(params, state);
}
}
};

watch(
() => [data.value, columns.value],
() => {
if (!data || !rowspanAndColspan) return;
updateSkipSpansMap(data.value, columns.value, rowspanAndColspan);
},
{ immediate: true },
);

return { skipSpansMap, updateSkipSpansMap };
}
29 changes: 6 additions & 23 deletions src/table/tbody.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { defineComponent, computed, PropType, SetupContext } from 'vue';
import { defineComponent, computed, PropType, SetupContext, toRefs } from 'vue';
import camelCase from 'lodash/camelCase';
import get from 'lodash/get';
import pick from 'lodash/pick';
import TrElement, { ROW_LISTENERS, TABLE_PROPS } from './tr';
import { useConfig } from '../hooks/useConfig';
import { RowspanColspan, TableRowData, BaseTableCellParams } from './type';
import { BaseTableProps, RowAndColFixedPosition } from './interface';
import { useTNodeJSX } from '../hooks/tnode';
import useClassName from './hooks/useClassName';
import baseTableProps from './base-table-props';
import { TNodeReturnValue } from '../common';
import useRowspanAndColspan from './hooks/useRowspanAndColspan';

export const ROW_AND_TD_LISTENERS = ROW_LISTENERS.concat('cell-click');
export interface TableBodyProps extends BaseTableProps {
Expand Down Expand Up @@ -87,8 +87,10 @@ export default defineComponent({
// eslint-disable-next-line
setup(props: TableBodyProps, { emit }: SetupContext) {
const renderTNode = useTNodeJSX();
const { data, columns } = toRefs(props);
const { t, global } = useConfig('table');
const { tableFullRowClasses, tableBaseClass } = useClassName();
const { skipSpansMap } = useRowspanAndColspan(data, columns, props.rowspanAndColspan);

const tbodyClasses = computed(() => [tableBaseClass.body]);

Expand All @@ -99,6 +101,7 @@ export default defineComponent({
tableFullRowClasses,
tbodyClasses,
tableBaseClass,
skipSpansMap,
};
},

Expand Down Expand Up @@ -139,28 +142,9 @@ export default defineComponent({
);
};

// 受合并单元格影响,部分单元格不显示
let skipSpansMap = new Map<any, boolean>();

const onTrRowspanOrColspan = (params: BaseTableCellParams<TableRowData>, cellSpans: RowspanColspan) => {
const { rowIndex, colIndex } = params;
if (!cellSpans.rowspan && !cellSpans.colspan) return;
const maxRowIndex = rowIndex + (cellSpans.rowspan || 1);
const maxColIndex = colIndex + (cellSpans.colspan || 1);
for (let i = rowIndex; i < maxRowIndex; i++) {
for (let j = colIndex; j < maxColIndex; j++) {
if (i !== rowIndex || j !== colIndex) {
skipSpansMap.set([i, j].join(), true);
}
}
}
};

const columnLength = this.columns.length;
const dataLength = this.data.length;
const trNodeList: TNodeReturnValue[] = [];
// 每次渲染清空合并单元格信息
skipSpansMap = new Map<any, boolean>();

const properties = [
'rowAndColFixedPosition',
Expand All @@ -181,10 +165,9 @@ export default defineComponent({
columns: this.columns,
rowIndex,
dataLength,
skipSpansMap,
skipSpansMap: this.skipSpansMap,
...pick(this.$props, properties),
// 遍历的同时,计算后面的节点,是否会因为合并单元格跳过渲染
onTrRowspanOrColspan,
};
if (this.onCellClick) {
trProps.onCellClick = this.onCellClick;
Expand Down
19 changes: 6 additions & 13 deletions src/table/tr.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { BaseTableCellParams, TableRowData, RowspanColspan, TdPrimaryTableProps,
import baseTableProps from './base-table-props';
import useLazyLoad from './hooks/useLazyLoad';
import { RowAndColFixedPosition } from './interface';
import { SkipSpansValue } from './hooks/useRowspanAndColspan';

export interface RenderTdExtra {
rowAndColFixedPosition: RowAndColFixedPosition;
Expand Down Expand Up @@ -64,8 +65,7 @@ export interface TrProps extends TrCommonProps {
rowIndex: number;
dataLength: number;
rowAndColFixedPosition?: RowAndColFixedPosition;
// 属性透传,引用传值,可内部改变
skipSpansMap?: Map<any, boolean>;
skipSpansMap?: Map<string, SkipSpansValue>;
scrollType?: string;
isVirtual?: boolean;
rowHeight?: number;
Expand All @@ -74,7 +74,6 @@ export interface TrProps extends TrCommonProps {
tableElm?: any;
// HTMLDivElement
tableContentElm?: any;
onTrRowspanOrColspan?: (params: PrimaryTableCellParams<TableRowData>, cellSpans: RowspanColspan) => void;
}

export const ROW_LISTENERS = ['click', 'dblclick', 'mouseover', 'mousedown', 'mouseenter', 'mouseleave', 'mouseup'];
Expand Down Expand Up @@ -107,8 +106,6 @@ export default defineComponent({
rowAndColFixedPosition: Map as PropType<RowAndColFixedPosition>,
// 合并单元格,是否跳过渲染
skipSpansMap: Map as PropType<TrProps['skipSpansMap']>,
// 扫描到 rowspan 或者 colspan 时触发
onTrRowspanOrColspan: Function as PropType<TrProps['onTrRowspanOrColspan']>,
...pick(baseTableProps, TABLE_PROPS),
scrollType: String,
rowHeight: Number,
Expand Down Expand Up @@ -273,14 +270,10 @@ export default defineComponent({
rowIndex,
colIndex,
};
if (isFunction(this.rowspanAndColspan)) {
const o = this.rowspanAndColspan(params);
o?.rowspan > 1 && (cellSpans.rowspan = o.rowspan);
o?.colspan > 1 && (cellSpans.colspan = o.colspan);
this.onTrRowspanOrColspan?.(params, cellSpans);
}
const skipped = this.skipSpansMap?.get([rowIndex, colIndex].join());
if (skipped) return null;
const spanState = this.skipSpansMap.get([rowIndex, colIndex].join()) || {};
spanState?.rowspan > 1 && (cellSpans.rowspan = spanState.rowspan);
spanState?.colspan > 1 && (cellSpans.colspan = spanState.colspan);
if (spanState.skipped) return null;
return this.renderTd(params, {
dataLength,
rowAndColFixedPosition,
Expand Down

0 comments on commit 0c02760

Please sign in to comment.