ivue-material-plus
Version:
A high quality UI components Library with Vue.js
413 lines (346 loc) • 11.4 kB
text/typescript
useRender;
import { h, inject, computed } from 'vue';
import useStyles from './styles';
import useEvents from './events';
import { getRowIdentity } from '../utils';
import { throwError } from '../../../utils/error';
// ts
import type { TableProps, RenderRowData, TreeNode } from '../table/defaults';
import type { TableBodyProps } from './defaults';
import type { TableColumnCtx } from '../table-column/defaults';
import { TableContextKey } from '../table/defaults';
function useRender(props: Partial<TableBodyProps>) {
// inject
const IvueTable = inject(TableContextKey);
// 样式
const { getRowClass, getRowStyle, getTableSpan, getCellClass, getCellStyle } =
useStyles(props);
// 事件
const {
handleClickTr,
handleCellMouseEnter,
handleCellMouseLeave,
handleMouseEnter,
handleMouseLeave,
} = useEvents(props);
// computed
// 是否是默认索引
const firstDefaultColumnIndex = computed(() => {
return props.store?.states.columns.value.findIndex(
({ type }) => type === 'default'
);
});
// methods
// 获取行key
const getRowKey = (row: TableColumnCtx, index: number) => {
const rowKey = (IvueTable.props as Partial<TableProps>).rowKey;
// 有自定义 rowKey
if (rowKey) {
// 获取rowKey对应的数据
return getRowIdentity(row, rowKey);
}
return index;
};
// 渲染行
const rowRender = (
row: TableColumnCtx,
$index: number,
treeRowData?: TreeNode,
// 展开状态
expanded = false
) => {
const { store } = props;
const { columns, indent } = store.states;
// 行样式
const rowClasses = getRowClass(row, $index);
let display = true;
// 是否是子节点数据
if (treeRowData) {
rowClasses.push(`ivue-table-level-${treeRowData.level}`);
display = treeRowData.display;
}
// tr 隐藏显示用于树形节点
const displayStyle = display
? null
: {
display: 'none',
};
return h(
'tr',
{
class: rowClasses,
style: [displayStyle, getRowStyle(row, $index)],
key: getRowKey(row, $index),
onClick: ($event) => {
handleClickTr($event, row);
},
// 鼠标进入
onMouseenter: () => {
handleMouseEnter($index);
},
// 鼠标离开
onMouseleave: () => {
handleMouseLeave();
},
},
columns.value.map((column, cellIndex) => {
// 获取单元格样式
const { rowspan, colspan } = getTableSpan(
row,
column,
$index,
cellIndex
);
// 是否存在单元格样式
if (!rowspan || !colspan) {
return null;
}
// key
const baseKey = `${$index},${cellIndex}`;
// 当前列的数据
const columnData = { ...column };
// column 的 key | 列 vode key
const patchKey = columnData.columnKey || columnData.rawColumnKey || '';
// data
const data: RenderRowData = {
store: props?.store,
_self: props.context || IvueTable,
column: columnData,
row,
$index,
cellIndex,
// 展开状态
expanded,
};
// 设置子节点数据
if (cellIndex === firstDefaultColumnIndex.value && treeRowData) {
data.treeNode = {
indent: treeRowData.level * indent.value,
level: treeRowData.level,
};
// 是否有展开
if (typeof treeRowData.expanded === 'boolean') {
data.treeNode.expanded = treeRowData.expanded;
// 表明是懒加载
if ('loading' in treeRowData) {
data.treeNode.loading = treeRowData.loading;
}
// 没有懒加载节点
if ('noLazyChildren' in treeRowData) {
data.treeNode.noLazyChildren = treeRowData.noLazyChildren;
}
}
}
return h(
'td',
{
// class
class: getCellClass($index, cellIndex, row, column),
// style
style: getCellStyle($index, cellIndex, row, column),
rowspan,
colspan,
// key
key: `${patchKey}${baseKey}`,
// 鼠标进入
onMouseenter: (event) => {
handleCellMouseEnter(event, row);
},
// 鼠标离开
onMouseleave: (event) => {
handleCellMouseLeave(event);
},
},
[
// 渲染单元格
column.renderCell(data),
]
);
})
);
};
// 渲染行
const wrappedRowRender = (row: TableColumnCtx, $index: number): any => {
const store = props.store;
const { isRowExpanded, assertRowKey } = store;
const { treeData, lazyTreeNodeMap, childrenColumnName, rowKey } =
store.states;
const columns = store?.states.columns.value;
// 是否是展开行
const hasExpandColumn = columns.some(({ type }) => type === 'expand');
// 是否是展开行
if (hasExpandColumn) {
// 是否展开行
const expanded = isRowExpanded(row);
// tr
const tr = rowRender(
row,
$index,
undefined,
// 展开状态
expanded
);
// 展开的内容
const renderExpanded = IvueTable.renderExpanded;
// 展开
if (expanded) {
// 没有展开内容
if (!renderExpanded) {
// eslint-disable-next-line no-console
console.error('[Error]renderExpanded is required.');
return tr;
}
return [
[
tr,
h(
'tr',
{
key: `expanded-row__${tr.key as string}`,
},
[
h(
'td',
{
colspan: columns.length,
class: ['ivue-table-cell', 'ivue-table--expand-cell'],
},
[
renderExpanded({
row,
$index,
store,
// 展开状态
expanded,
}),
]
),
]
),
],
];
}
// 未展开
else {
return [[tr]];
}
}
// 子节点渲染
else if (Object.keys(treeData.value).length) {
// 检查 rowKey 是否存在
assertRowKey();
// TreeTable 时,rowKey 必须由用户设定,不使用 getKeyOfRow 计算
// 在调用 rowRender 函数时,仍然会计算 rowKey,
// 获取rowKey对应的数据
const rowKeyData = getRowIdentity(row, rowKey.value);
// 子节点数据
let childrenData = treeData.value[rowKeyData];
// 每一行的节点数据
let treeRowData = null;
// 子节点数据
if (childrenData) {
treeRowData = {
expanded: childrenData.expanded,
level: childrenData.level,
display: true,
};
// 懒加载数据
if (typeof childrenData.lazy === 'boolean') {
// 加载完成
if (typeof childrenData.loaded === 'boolean' && childrenData.loaded) {
// 没有子节点
treeRowData.noLazyChildren = !(
childrenData.children && childrenData.children.length
);
}
// 加载
treeRowData.loading = childrenData.loading;
}
}
const dom = [rowRender(row, $index, treeRowData)];
// 渲染嵌套数据
if (childrenData) {
// currentRow 记录的是 index,所以还需主动增加 TreeTable 的 index
let i = 0;
// 递归方法
const traverse = (children, parent) => {
// 没有数据
if (!(children && children.length && parent)) {
return;
}
children.forEach((node) => {
// 父节点的 display 状态影响子节点的显示状态
const innerTreeRowData = {
display: parent.display && parent.expanded,
level: parent.level + 1,
expanded: false,
noLazyChildren: false,
loading: false,
};
// 获取rowKey对应的数据
const childKey = getRowIdentity(node, rowKey.value);
if (childKey === undefined || childKey === null) {
throwError(
'ivue-table',
'For nested data item, row-key is required'
);
}
childrenData = { ...treeData.value[childKey] };
// 对于当前节点,分成有无子节点两种情况。
// 如果包含子节点的,设置 expanded 属性。
// 对于它子节点的 display 属性由它本身的 expanded 与 display 共同决定。
if (childrenData) {
innerTreeRowData.expanded = childrenData.expanded;
// 懒加载的某些节点,level 未知
childrenData.level = childrenData.level || innerTreeRowData.level;
childrenData.display = !!(
childrenData.expanded && innerTreeRowData.display
);
// 懒加载
if (typeof childrenData.lazy === 'boolean') {
// 加载完成
if (
typeof childrenData.loaded === 'boolean' &&
childrenData.loaded
) {
// 没有子节点
innerTreeRowData.noLazyChildren = !(
childrenData.children && childrenData.children.length
);
}
// 加载中
innerTreeRowData.loading = childrenData.loading;
}
}
i++;
// 添加节点
dom.push(rowRender(node, $index + i, innerTreeRowData));
if (childrenData) {
// 懒加载节点 | 子节点 = 获取行数据
const nodes =
lazyTreeNodeMap.value[childKey] ||
node[childrenColumnName.value];
traverse(nodes, childrenData);
}
});
};
// 对于 root 节点,display 一定为 true
childrenData.display = true;
// 懒加载节点 | 子节点 = 获取行数据
const nodes =
lazyTreeNodeMap.value[rowKeyData] || row[childrenColumnName.value];
// 递归方法
traverse(nodes, childrenData);
}
return dom;
}
// 普通渲染
else {
return rowRender(row, $index);
}
};
return {
wrappedRowRender,
};
}
export default useRender;