UNPKG

ng-devui

Version:

DevUI components based on Angular

876 lines 215 kB
import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling'; import { DOCUMENT } from '@angular/common'; import { ChangeDetectorRef, Component, ContentChild, ContentChildren, ElementRef, EventEmitter, forwardRef, HostBinding, Inject, Input, NgZone, Output, QueryList, Renderer2, TemplateRef, ViewChild, } from '@angular/core'; import { merge } from 'rxjs'; import { switchMap, takeUntil } from 'rxjs/operators'; import { DataTableHeadComponent } from './data-table-head.component'; import { DATA_TABLE } from './data-table.token'; import { TableTbodyComponent } from './table/body/tbody.component'; import { TableThComponent } from './table/head/th/th.component'; import { TableTheadComponent } from './table/head/thead.component'; import { DataTableColumnTmplComponent } from './tmpl/data-table-column-tmpl.component'; import * as i0 from "@angular/core"; import * as i1 from "@angular/common"; import * as i2 from "@angular/cdk/scrolling"; import * as i3 from "ng-devui/dragdrop"; import * as i4 from "ng-devui/utils"; import * as i5 from "./data-table-body.component"; import * as i6 from "./data-table-head.component"; import * as i7 from "./table/body/tbody.component"; const SCROLL_BAR_WIDTH = 8; export class DataTableComponent { get hostHeight() { return this.tableHeight && this.dataSource.length ? this.tableHeight : null; } get hasShadow() { return this.shadowType === 'normal'; } set content(content) { setTimeout(() => { this.tableBodyEl = content; if (this.virtualScroll) { this.initVirtualBodyHeight(); } }); } set dataSource(dataSource) { if (dataSource === null || !dataSource) { /* eslint-disable-next-line no-param-reassign */ dataSource = []; } this._dataSource = dataSource; const hasChecked = this.dataSource.some(this.hasChecked); const hasUnChecked = this.dataSource.some(this.hasUnChecked); this._pageAllChecked = dataSource && dataSource.length > 0 && !hasUnChecked; this.halfChecked = hasChecked && hasUnChecked; if (this.innerHeader) { this.innerHeader.setHeaderCheckStatus({ pageAllChecked: this._pageAllChecked, pageHalfChecked: this.halfChecked }); } // 固定表头时,数据从无数据到有数据,需要更新滚动位置,避免对齐偏移 if (this.fixHeader) { setTimeout(() => { this.onBodyScroll(); }); } if (this.virtualScroll) { this.initVirtualBodyHeight(); } this.initScrollStatus(); } get dataSource() { return this._dataSource; } set hideColumn(hideColumn) { this._hideColumn = hideColumn; if (this._columns) { this.updateColumns(); } } get hideColumn() { return this._hideColumn; } set pageAllChecked(pageAllChecked) { if (this.dataSource) { this._dataSource = this.setCheckedStatus(this.dataSource, pageAllChecked); } this._pageAllChecked = pageAllChecked; this.halfChecked = this.dataSource.some(this.hasChecked) && this.dataSource.some(this.hasUnChecked); } get pageAllChecked() { return this._pageAllChecked; } constructor(elementRef, ngZone, renderer, cdr, doc) { this.elementRef = elementRef; this.ngZone = ngZone; this.renderer = renderer; this.cdr = cdr; this.doc = doc; /** * 【可选】表头checkbox是否可见 */ this.headerCheckVisible = true; this.checkOptionsIndex = 1050; this.selectOptionOnCheckbox = false; /** * 【可选】默认表格使用的表格类型,可选值为'cell' @deprecated */ this.editModel = 'cell'; /** * 鼠标悬浮行时是否高亮 */ this.rowHoveredHighlight = true; /** * 表格宽度 */ this.tableWidth = '100%'; /** * 固定表头指定高度是否包含表头的高度,`tableHeight`设置的高度默认是表格body的高度 */ this.containFixHeaderHeight = false; /** * 【可选】多列选择数组,用来指导那几列会被排序 */ this.multiSort = []; /** * 【可选】用来自定义不可拖拽的前几列 */ this.colDropFreezeTo = 0; /** * 【可选】同时绑定单击、双击事件时,用于区分点击的时间间隔,默认300ms,两个事件不同时使用可以指定为0 */ this.timeout = 300; /** * 【可选】配置表头操作未激活状态下是否显示操作区域,默认不显示 */ this.showOperationArea = false; /** * 【可选】是否显示排序未激活图标,默认显示, */ this.showSortIcon = true; /** * 【可选】是否显示筛选未激活图标,默认显示, */ this.showFilterIcon = true; /** * 多列选择Change事件,用来更新多列选择数组, column param * */ this.multiSortChange = new EventEmitter(); /** * 表格单元格点击事件 */ this.cellClick = new EventEmitter(); /** * 表格单元格双击事件 */ this.cellDBClick = new EventEmitter(); /** * 表格行点击事件 */ this.rowClick = new EventEmitter(); /** * 表格行双击事件 */ this.rowDBClick = new EventEmitter(); /** * 行detail toggle事件 */ this.detialToggle = new EventEmitter(); /** * 表格单元格开始编辑事件 */ this.cellEditStart = new EventEmitter(); /** * 表格单元格结束编辑事件 */ this.cellEditEnd = new EventEmitter(); /** * 某行的勾选状态变化事件 */ this.rowCheckChange = new EventEmitter(); /** * 当前页码全勾选状态变化事件 */ this.checkAllChange = new EventEmitter(); /** * 延迟懒加载完成事件 */ this.loadMore = new EventEmitter(); /** * 列宽变化事件 */ this.resize = new EventEmitter(); /** * 当前表格层级,默认为0,在树形表格场景下自增长 * 内部嵌套使用,不对外暴露 */ this.tableLevel = 0; /** * 配置树形表格的父子选中是否互相关联 * upward:选中子关联父 * downward: 选中父关联子 */ this.checkableRelation = { upward: true, downward: true }; /** * 子列表关闭事件 */ this.childrenTableClose = new EventEmitter(); /** * 全部子列表关闭事件 */ this.allChildrenTableClose = new EventEmitter(); /** * 虚拟滚动配置 */ this.virtualItemSize = 40; this.virtualMinBufferPx = 80; this.virtualMaxBufferPx = 200; /** * 列宽配置 */ this.tableWidthConfig = []; /** * 表格布局 */ this.tableLayout = 'fixed'; /** * 表格边框类型,默认有行边框,bordered:全边框,borderless:无边框 */ this.borderType = ''; /** * 表格内部滚动事件 */ this.tableScrollEvent = new EventEmitter(); this.columnDragEnd = new EventEmitter(); /** * 表格尺寸,sm对应行高40px, md对应行高48px,lg对应行高56px */ this.size = 'sm'; this.shadowType = 'embed'; this.tableOverflowType = 'auto'; this._dataSource = []; this._pageAllChecked = false; this.selectable = true; this.allChecked = []; this.documentClickEvent = new EventEmitter(); this.cellEditorClickEvent = new EventEmitter(); this._hideColumn = []; this.searchQueryChange = new EventEmitter(); this.halfChecked = false; this.scrollY = 0; this.BUILTIN_COL_WIDTH = '41px'; this.BUILTIN_COL_WIDTH_EXTRA = '55px'; this._tableTotalWidth = 0; this._lastColSize = 0; // 判断数据是否存在选中状态 this.hasChecked = (data) => { if (data.$checked) { return true; } if (data.children) { return data.children.some(this.hasChecked); } }; // 判断数据是否存在未选中状态 this.hasUnChecked = (data) => { if (!data.$checked) { return true; } if (data.children) { return data.children.some(this.hasUnChecked); } }; this.onDocumentClickListen = this.onDocumentClick.bind(this); this.document = this.doc; } initVirtualBodyHeight() { setTimeout(() => { if (this.virtualScrollViewport) { this.virtualScrollViewport.checkViewportSize(); } }); if (this.tableHeight && this.tableHeight !== 'auto') { this.virtualBodyHeight = this.tableHeight; return; } if (!this.maxHeight) { this.virtualBodyHeight = null; return; } if (this.tableBodyEl) { const tableHeader = this.tableBodyEl.nativeElement.querySelector('thead'); const tableHeaderHeight = tableHeader?.offsetHeight + SCROLL_BAR_WIDTH || 0; const curTotalHeight = this.dataSource.length * this.virtualItemSize + tableHeaderHeight + SCROLL_BAR_WIDTH; this.virtualBodyHeight = curTotalHeight < parseInt(this.maxHeight, 10) ? curTotalHeight + 'px' : this.maxHeight; return; } } getColumns() { const cols = this.columns.filter((column) => { return !this.hideColumn.some((field) => column.field === field); }); cols.sort((first, second) => first.order - second.order); return cols; } // life hook start ngOnInit() { this.ngZone.runOutsideAngular(() => { this.document.addEventListener('click', this.onDocumentClickListen); }); } ngOnChanges(changes) { const { checkable, showExpandToggle, tableHeight, maxHeight, virtualScroll } = changes; if (checkable) { const checkColIndex = this.tableWidthConfig.findIndex((config) => { return config.field === 'checkbox'; }); if (this.checkable) { if (checkColIndex < 0) { if (this.showExpandToggle) { this.tableWidthConfig.splice(1, 0, { field: 'checkbox', width: this.BUILTIN_COL_WIDTH }); } else { this.tableWidthConfig.unshift({ field: 'checkbox', width: this.BUILTIN_COL_WIDTH }); } } } else { if (checkColIndex > -1) { this.tableWidthConfig.splice(checkColIndex, 1); } } } if (showExpandToggle) { const expandColIndex = this.tableWidthConfig.findIndex((config) => { return config.field === 'expand'; }); if (this.showExpandToggle) { if (expandColIndex < 0) { this.tableWidthConfig.unshift({ field: 'expand', width: this.BUILTIN_COL_WIDTH }); } } else { if (expandColIndex > -1) { this.tableWidthConfig.splice(expandColIndex, 1); } } } if (this.virtualScroll && ((tableHeight && !tableHeight.firstChange) || (maxHeight && !maxHeight.firstChange) || (virtualScroll && !virtualScroll.firstChange))) { this.initVirtualBodyHeight(); } } onDocumentClick($event) { this.documentClickEvent.emit($event); } ngAfterContentInit() { if (this.columns.length > 0) { this.updateColumns(); this.columns.forEach((col) => { col.orderChange.subscribe(() => { this.updateColumns(); }); col.widthChange.subscribe(() => { this.updateColumns(); }); }); } if (this.innerHeader) { this.headerCheckStatusSubscription = this.innerHeader.headerCheckStatusEvent.subscribe((status) => { this.onCheckAllChange(status); }); this.headertoggleTableSubscription = this.innerHeader.headerChildrenTableToggleEvent.subscribe((status) => { this.onToggleAllChildrenTable(status); }); } else { this.columns.changes.subscribe(() => { this.updateColumns(); }); } setTimeout(() => { this.initScrollStatus(); }); } initScrollStatus() { if (this.tableOverflowType !== 'overlay') { return; } const ele = this.virtualScroll ? this.virtualScrollViewport?.elementRef?.nativeElement : this.normalScrollElement?.nativeElement; this.hasWidthScroll = ele && ele.scrollWidth > ele.clientWidth; this.hasHeightScroll = ele && ele.scrollHeight > ele.clientHeight; } ngAfterViewInit() { this.thList.forEach((th) => { th.tableViewRefElement = this.tableViewRefElement; }); this.thList.changes.subscribe((list) => { list.forEach((th) => { th.tableViewRefElement = this.tableViewRefElement; }); }); if (this.onlyOneColumnSort) { this.resetThSortOrder(); } setTimeout(() => { this.initScrollClass(); }); } // 初始化时判断是否存在横向滚动,并加上相应类名 initScrollClass() { const ele = this.normalScrollElement?.nativeElement || this.vitualScrollElement?.elementRef?.nativeElement; if (ele.clientWidth !== ele.scrollWidth) { this.setScrollViewClass('left'); } } resetThSortOrder() { merge(...this.thList?.map((th) => th.sortChange)) .pipe(takeUntil(this.thList.changes)) .subscribe((sortEvent) => { this.thList.filter((th) => th !== sortEvent.th).forEach((th) => th.clearSortOrder()); }); this.thList.changes.pipe(switchMap(() => merge(...this.thList.map((th) => th.sortChange)))).subscribe((sortEvent) => { this.thList.filter((th) => th !== sortEvent.th).forEach((th) => th.clearSortOrder()); }); } updateColumns() { this._columns = this.getColumns(); this.tableWidthConfig = []; if (this.showExpandToggle) { this.tableWidthConfig.push({ field: 'expand', width: this.BUILTIN_COL_WIDTH }); } if (this.checkable) { if (this.checkOptions && this.checkOptions.length > 0) { this.tableWidthConfig.push({ field: 'checkbox', width: this.BUILTIN_COL_WIDTH_EXTRA }); } else { this.tableWidthConfig.push({ field: 'checkbox', width: this.BUILTIN_COL_WIDTH }); } } this._columns.forEach((col) => { this.tableWidthConfig.push({ field: col.field, width: col.width }); }); } ngOnDestroy() { this.unSubscription(); this.document.removeEventListener('click', this.onDocumentClickListen); } onHandleSort(column) { if (this.multiSort && this.multiSort.length > 0) { const multiSortIndex = this.multiSort.findIndex((item) => item.field === column.field); if (multiSortIndex !== -1) { this.multiSort.splice(multiSortIndex, 1); } if (column.direction === '') { this.multiSortChange.emit(this.multiSort); return; } if (!this.onlyOneColumnSort) { this.multiSort.push(column); } else { this.multiSort = [column]; } } else { this.multiSort = [column]; } this.multiSortChange.emit(this.multiSort); } onCellClick($event) { this.selectedRowItem = $event.rowItem; this.selectedColumnItem = $event.column; this.ngZone.run(() => { this.cellClick.emit($event); }); } onCellEditStart($event) { this.isCellEdit = true; this.cellEditStart.emit($event); } onCellEditEnd($event) { this.isCellEdit = false; this.cellEditEnd.emit($event); } onCellDBClick($event) { this.ngZone.run(() => { this.cellDBClick.emit($event); }); } onRowClick($event) { this.selectedRowItem = $event.rowItem; this.ngZone.run(() => { this.rowClick.emit($event); }); } onRowDBClick($event) { this.ngZone.run(() => { this.rowDBClick.emit($event); }); } onDetailToggle($event) { this.detialToggle.emit($event); } setRowCheckStatus($event) { // 处理children的选中 if ($event.rowItem.children && this.checkableRelation.downward) { this.setCheckedStatus($event.rowItem.children, $event.checked); } // 处理parents的选中 if (this.checkableRelation.upward) { const nestedIndexArray = $event.nestedIndex.split(','); nestedIndexArray.shift(); const nestedIndexArrayToInt = nestedIndexArray.map((value) => { return parseInt(value, 10); }); // 通过选中行的父级索引设置父的选中状态 this.setParentCheckStatus(nestedIndexArrayToInt); } // 处理整个table header的选中 const hasChecked = this.dataSource.some(this.hasChecked); if ($event) { const hasUnChecked = this.dataSource.some(this.hasUnChecked); this._pageAllChecked = !hasUnChecked; this.halfChecked = hasChecked && hasUnChecked; } else { this._pageAllChecked = false; this.halfChecked = hasChecked; } if (this.innerHeader) { this.innerHeader.setHeaderCheckStatus({ pageAllChecked: this._pageAllChecked, pageHalfChecked: this.halfChecked }); } this.rowCheckChange.emit($event); } setParentCheckStatus(nestedIndex) { if (nestedIndex.length > 0) { const topIndex = nestedIndex[0]; const topParent = this.dataSource[topIndex]; const argNestedIndex = [...nestedIndex]; argNestedIndex.shift(); const lastParent = this.findLastParent(topParent, argNestedIndex); this.setSelfCheckStatus(lastParent); nestedIndex.pop(); if (nestedIndex.length > 0) { this.setParentCheckStatus(nestedIndex); } } } findLastParent(source, indexArray) { if (source && indexArray.length > 0) { const topIndex = indexArray[0]; const topParent = source.children[topIndex]; indexArray.shift(); return this.findLastParent(topParent, indexArray); } else { return source; } } setSelfCheckStatus(data) { if (data && data.children) { const hasUnChecked = data.children.some((child) => { return !child.$checked; }); const hasChecked = data.children.some((child) => { return child.$checked || child.$halfChecked; }); data.$checked = !hasUnChecked; data.$halfChecked = hasUnChecked && hasChecked; } } setCheckedStatus(data, checked, toggle) { return data.map((item) => { if (!(item.$checkDisabled || item.$disabled)) { if ((toggle && item.$checked === undefined) || !toggle) { item.$checked = checked; item.$halfChecked = false; } } if (item.children) { item.children = this.setCheckedStatus(item.children, checked, toggle); } return item; }); } onCheckAllChange($event) { this.pageAllChecked = $event; this.checkAllChange.emit($event); } onSearchQueryChange($event) { this.searchQueryChange.emit($event); } getSelectedRowItem() { return this.selectedRowItem; } onLoadMore(event) { this.loadMore.emit(this); } updateColumnsWidth() { this.tableWidthConfig = []; if (this.showExpandToggle) { const expandWidth = this.elementRef.nativeElement.querySelector('.devui-detail-cell').clientWidth; this.tableWidthConfig.push({ field: 'expand', width: expandWidth + 'px' }); } if (this.checkable) { const list = this.elementRef.nativeElement.querySelectorAll('.devui-checkable-cell'); const checkboxWidth = list[list.length - 1].clientWidth; this.tableWidthConfig.push({ field: 'checkbox', width: checkboxWidth + 'px' }); } this._columns.forEach((col) => { this.tableWidthConfig.push({ field: col.field, width: col.width }); }); } beginResizeHandlerEvent($event) { const thRenderWidthList = $event.thRenderWidthList; if (thRenderWidthList.length > 0) { this._tableTotalWidth = this.elementRef.nativeElement.querySelector('.table-wrap').offsetWidth - 8; // 兼容d-column表头分组场景 const reverseThList = thRenderWidthList.reverse(); this._columns.forEach((column) => { const thItem = reverseThList.find((th) => th.field === column.field); if (thItem) { column.width = thItem.width + 'px'; } }); if (!this._lastColSize) { this._lastColSize = parseInt(this._columns.slice(-1)[0].width, 10); } this.updateColumnsWidth(); } this.onDocumentClick($event.event); } onResizingFixedHandler({ field, width }) { if (this.resizeable) { const index = this.tableWidthConfig.findIndex((config) => { return config.field === field; }); if (index > -1) { setTimeout(() => { this.tableWidthConfig[index].width = width + 'px'; }); } } } onResizeHandler({ width, field }) { if (width < 0) { return; } const index = this.tableWidthConfig.findIndex((config) => { return config.field === field; }); if (index > -1) { this.tableWidthConfig[index].width = width + 'px'; } const curTotal = this.tableWidthConfig.reduce((pre, cur) => { const value = pre + parseInt(cur.width, 10); return value; }, 0); let columnResizeEventArg; this._columns = this._columns.map((column, colIndex) => { if (column.field === field) { column.width = parseInt(width, 10) + 'px'; columnResizeEventArg = { currentColumn: column, nextColumn: this._columns[colIndex + 1] }; this.resize.emit(columnResizeEventArg); } return column; }); const changeSize = curTotal - this._tableTotalWidth; const lastCol = this._columns.slice(-1)[0]; const lastColWidth = parseInt(lastCol.width, 10); if (changeSize < 0 && columnResizeEventArg.nextColumn) { const newSize = parseInt(lastCol.width) - changeSize + 'px'; lastCol.width = newSize; this.tableWidthConfig[this.tableWidthConfig.length - 1].width = newSize; } else if (this._lastColSize < lastColWidth) { const lastChange = lastColWidth - this._lastColSize > changeSize ? changeSize : lastColWidth - this._lastColSize; lastCol.width = lastColWidth - lastChange + 'px'; this.tableWidthConfig[this.tableWidthConfig.length - 1].width = lastColWidth - lastChange + 'px'; } } handleDragTable({ from, to }) { this.columnDragEnd.emit({ from, to }); const sortArray = (array, fromE, toE) => { if (fromE < toE) { const fromEData = array[fromE]; for (let i = 0; i < array.length; i++) { if (i >= fromE && i < toE) { this.ngZone.run(() => { array[i] = array[i + 1]; }); } } this.ngZone.run(() => { array[toE] = fromEData; }); } if (fromE > toE) { const fromEData = array[fromE]; for (let i = array.length; i > 0; i--) { if (i <= fromE && i > toE) { array[i] = array[i - 1]; } } array[toE] = fromEData; } }; sortArray(this._columns, from, to); this._columns.forEach((item, index) => { item.order = index; }); } onBodyScroll(event) { const target = event?.target || this.normalScrollElement?.nativeElement || this.virtualScrollViewport?.elementRef.nativeElement; if (this.isCellEdit) { // Y轴滚动距离超过tr高度时取消目前编辑状态 if (this.scrollY === 0) { this.scrollY = target.scrollTop; } const offset = target.scrollTop - this.scrollY; if (offset > 40 || offset < -40) { this.cancelEditingStatus(); this.scrollY = 0; } } const scrollLeft = target.scrollLeft; if (scrollLeft === 0) { if (target.clientWidth === target.scrollWidth) { this.setScrollViewClass('none'); } else { this.setScrollViewClass('left'); } } else if (Math.abs(scrollLeft + target.clientWidth - target.scrollWidth) < 1) { this.setScrollViewClass('right'); } else { this.setScrollViewClass('middle'); } if (this.fixHeader) { this.fixHeaderContainerRefElement.nativeElement.scrollLeft = scrollLeft; } this.tableScrollEvent.emit(event); } setScrollViewClass(position) { const element = this.tableViewRefElement.nativeElement; const className = 'devui-table-scroll-' + position; const elClassList = element.classList; if (!elClassList.contains(className)) { for (let index = 0; index < elClassList.length; index++) { const clName = elClassList[index]; if (clName.startsWith('devui-table-scroll-')) { this.renderer.removeClass(element, clName); } } this.renderer.addClass(element, className); } } unSubscription() { if (this.headerCheckStatusSubscription) { this.headerCheckStatusSubscription.unsubscribe(); this.headerCheckStatusSubscription = null; } if (this.headertoggleTableSubscription) { this.headertoggleTableSubscription.unsubscribe(); this.headertoggleTableSubscription = null; } } setRowChildToggleStatus(rowItem, open) { if (open) { let loadChildrenResult = Promise.resolve(true); if (this.loadChildrenTable) { loadChildrenResult = this.loadChildrenTable(rowItem); } loadChildrenResult.then(() => { // 异步加载子表格是检查选中状态 if (rowItem.$checked && this.checkableRelation.downward) { this.setCheckedStatus(rowItem.children, rowItem.$checked, true); } }); } else { this.childrenTableClose.emit(rowItem); } } setTableChildrenToggleStatus(open) { this.onToggleAllChildrenTable(open); if (this.innerHeader) { this.innerHeader.setHeaderToggleStatus(open); } else { this.childrenTableOpen = open; } } travelChildrenToggleStatus(data, open) { return data.map((item) => { if (item.children) { item.$isChildTableOpen = open; item.children = this.travelChildrenToggleStatus(item.children, open); } return item; }); } // 切换表头的子表格展开收起 onToggleAllChildrenTable(open) { this.childrenTableOpen = open; if (open) { let loadAllChildrenResult = Promise.resolve(true); if (this.loadAllChildrenTable) { loadAllChildrenResult = this.loadAllChildrenTable(); } loadAllChildrenResult.then(() => { this.dataSource.forEach((item) => { if (item.$checked && item.children) { this.setCheckedStatus(item.children, true, true); } }); this.travelChildrenToggleStatus(this.dataSource, open); }); } else { this.travelChildrenToggleStatus(this.dataSource, open); this.allChildrenTableClose.emit(); } } cancelEditingStatus() { this.documentClickEvent.emit('cancel'); } collectCheckedRows(dist, source) { source.forEach((row) => { if (row.$checked) { dist.push(row); } if (row.children) { this.collectCheckedRows(dist, row.children); } }); } getCheckedRows() { if (this.checkableRelation.upward) { // 如果children的选中状态关联parent的选中状态,只需返回最外层的数据 return this.dataSource ? this.dataSource.filter((item) => item.$checked || item.$halfChecked) : []; } else { // 如果children的选中状态不关联parent的选中状态,遍历dataSource,将所有的选中行平级返回 const checkedRows = []; this.collectCheckedRows(checkedRows, this.dataSource); return checkedRows; } } setTableCheckStatus(status) { if (status.pageAllChecked !== undefined) { // 设置全选 if (this.dataSource) { this._dataSource = this.setCheckedStatus(this.dataSource, status.pageAllChecked); } this._pageAllChecked = status.pageAllChecked; if (status.pageAllChecked) { // 全选为true this.halfChecked = false; } else { this.halfChecked = this.dataSource.some(this.hasChecked) && this.dataSource.some(this.hasUnChecked); } } if (status.pageHalfChecked !== undefined) { // 设置半选 this.halfChecked = status.pageHalfChecked; } if (this.innerHeader) { this.innerHeader.setHeaderCheckStatus({ pageAllChecked: this._pageAllChecked, pageHalfChecked: this.halfChecked }); } } // 更新cdk虚拟滚动viewport size并重新渲染,解决父层高度变化渲染数据size没有更新问题 updateVirtualScrollSize() { this.virtualScrollViewport.checkViewportSize(); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DataTableComponent, deps: [{ token: i0.ElementRef }, { token: i0.NgZone }, { token: i0.Renderer2 }, { token: i0.ChangeDetectorRef }, { token: DOCUMENT }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: DataTableComponent, selector: "d-data-table", inputs: { checkable: "checkable", headerCheckDisabled: "headerCheckDisabled", headerCheckVisible: "headerCheckVisible", checkOptions: "checkOptions", checkOptionsIndex: "checkOptionsIndex", selectOptionOnCheckbox: "selectOptionOnCheckbox", showExpandToggle: "showExpandToggle", fixHeader: "fixHeader", scrollable: "scrollable", editModel: "editModel", maxWidth: "maxWidth", maxHeight: "maxHeight", type: "type", rowHoveredHighlight: "rowHoveredHighlight", generalRowHoveredData: "generalRowHoveredData", cssClass: "cssClass", tableWidth: "tableWidth", tableHeight: "tableHeight", containFixHeaderHeight: "containFixHeaderHeight", onlyOneColumnSort: "onlyOneColumnSort", multiSort: "multiSort", resizeable: "resizeable", colDraggable: "colDraggable", colDropFreezeTo: "colDropFreezeTo", detailTemplateRef: "detailTemplateRef", timeout: "timeout", showOperationArea: "showOperationArea", showSortIcon: "showSortIcon", showFilterIcon: "showFilterIcon", beforeCellEdit: "beforeCellEdit", beforeCellEditEnd: "beforeCellEditEnd", tableLevel: "tableLevel", checkableRelation: "checkableRelation", loadChildrenTable: "loadChildrenTable", loadAllChildrenTable: "loadAllChildrenTable", virtualScroll: "virtualScroll", headerExpandConfig: "headerExpandConfig", virtualItemSize: "virtualItemSize", virtualMinBufferPx: "virtualMinBufferPx", virtualMaxBufferPx: "virtualMaxBufferPx", lazy: "lazy", tableWidthConfig: "tableWidthConfig", headerBg: "headerBg", tableLayout: "tableLayout", borderType: "borderType", striped: "striped", minHeight: "minHeight", size: "size", shadowType: "shadowType", tableOverflowType: "tableOverflowType", dataSource: "dataSource", hideColumn: "hideColumn", pageAllChecked: "pageAllChecked" }, outputs: { multiSortChange: "multiSortChange", cellClick: "cellClick", cellDBClick: "cellDBClick", rowClick: "rowClick", rowDBClick: "rowDBClick", detialToggle: "detialToggle", cellEditStart: "cellEditStart", cellEditEnd: "cellEditEnd", rowCheckChange: "rowCheckChange", checkAllChange: "checkAllChange", loadMore: "loadMore", resize: "resize", childrenTableClose: "childrenTableClose", allChildrenTableClose: "allChildrenTableClose", tableScrollEvent: "tableScrollEvent", columnDragEnd: "columnDragEnd" }, host: { properties: { "style.height": "this.hostHeight", "class.devui-table-shadow": "this.hasShadow" } }, providers: [ { provide: DATA_TABLE, useExisting: forwardRef(() => DataTableComponent), }, ], queries: [{ propertyName: "innerHeader", first: true, predicate: TableTheadComponent, descendants: true }, { propertyName: "innerBody", first: true, predicate: TableTbodyComponent, descendants: true }, { propertyName: "noResultTemplate", first: true, predicate: ["noResultTemplateRef"], descendants: true }, { propertyName: "columns", predicate: DataTableColumnTmplComponent }, { propertyName: "thList", predicate: TableThComponent, descendants: true }], viewQueries: [{ propertyName: "fixHeaderContainerRefElement", first: true, predicate: ["fixHeaderContainerRef"], descendants: true }, { propertyName: "tableViewRefElement", first: true, predicate: ["tableView"], descendants: true, static: true }, { propertyName: "virtualScrollViewport", first: true, predicate: ["cdkVirtualScrollViewport"], descendants: true }, { propertyName: "normalScrollElement", first: true, predicate: ["normalScroll"], descendants: true }, { propertyName: "vitualScrollElement", first: true, predicate: ["scrollViewTpl"], descendants: true }, { propertyName: "columnHeaderComponent", first: true, predicate: DataTableHeadComponent, descendants: true }, { propertyName: "devuiNormalScrollBody", first: true, predicate: ["devuiNormalScrollBody"], descendants: true, read: ElementRef }, { propertyName: "content", first: true, predicate: ["tableBody"], descendants: true }], exportAs: ["dataTable"], usesOnChanges: true, ngImport: i0, template: "<div class=\"devui-data-table {{ cssClass }}\">\r\n <div\r\n class=\"devui-table-view {{ containFixHeaderHeight ? 'contain-fix-height' : '' }}\"\r\n [ngClass]=\"{\r\n 'has-width-scroll': hasWidthScroll,\r\n 'has-height-scroll': hasHeightScroll\r\n }\"\r\n #tableView\r\n >\r\n <div\r\n *ngIf=\"fixHeader\"\r\n #fixHeaderContainerRef\r\n class=\"table-wrap devui-fix-header\"\r\n [ngClass]=\"{\r\n 'shadow-fix-header': headerBg\r\n }\"\r\n [style.overflow-x]=\"dataSource?.length ? 'hidden' : 'auto'\"\r\n [style.overflow-y]=\"dataSource?.length ? 'scroll' : 'hidden'\"\r\n [style.max-height]=\"maxHeight ? maxHeight : null\"\r\n [style.max-width]=\"maxWidth ? maxWidth : null\"\r\n [style.width]=\"!maxWidth ? tableWidth : null\"\r\n >\r\n <table\r\n class=\"devui-table devui-table-{{ size }} {{ borderType ? 'table-' + borderType : '' }} {{ fixHeader ? 'table-fix-header' : '' }} {{\r\n headerBg ? 'header-bg' : ''\r\n }} {{ striped ? 'table-striped' : '' }}\"\r\n [ngClass]=\"{ 'area-active': showOperationArea }\"\r\n [style.table-layout]=\"tableLayout\"\r\n >\r\n <colgroup>\r\n <col *ngFor=\"let config of tableWidthConfig\" [style.width]=\"config.width\" />\r\n </colgroup>\r\n <ng-container *ngTemplateOutlet=\"HeaderContent\"></ng-container>\r\n <ng-template *ngIf=\"!innerHeader\" [ngTemplateOutlet]=\"headerTpl\"></ng-template>\r\n </table>\r\n </div>\r\n\r\n <div\r\n #normalScroll\r\n cdkScrollable\r\n dLazyLoad\r\n [enableLazyLoad]=\"lazy\"\r\n (loadMore)=\"onLoadMore($event)\"\r\n *ngIf=\"!virtualScroll; else scrollViewTpl\"\r\n class=\"devui-scrollbar\"\r\n [style.overflow-x]=\"dataSource?.length || !fixHeader ? 'auto' : 'hidden'\"\r\n [ngClass]=\"{\r\n 'scroll-view': scrollable,\r\n 'overflow-overlay': tableOverflowType === 'overlay',\r\n 'table-wrap': !fixHeader\r\n }\"\r\n [style.height]=\"tableHeight ? tableHeight : null\"\r\n [style.max-height]=\"maxHeight ? maxHeight : null\"\r\n [style.max-width]=\"maxWidth ? maxWidth : null\"\r\n [style.width]=\"!maxWidth ? tableWidth : null\"\r\n (scroll)=\"onBodyScroll($event)\"\r\n >\r\n <table\r\n #tableBody\r\n class=\"devui-table devui-table-{{ size }} {{ borderType ? 'table-' + borderType : '' }} {{\r\n rowHoveredHighlight ? 'table-hover' : ''\r\n }} {{ headerBg ? 'header-bg' : '' }} {{ striped ? 'table-striped' : '' }}\"\r\n [ngClass]=\"{ 'area-active': showOperationArea }\"\r\n [style.table-layout]=\"tableLayout\"\r\n [style.height]=\"minHeight ? minHeight : null\"\r\n [style.position]=\"tableOverflowType === 'overlay' ? null : 'relative'\"\r\n >\r\n <colgroup>\r\n <col *ngFor=\"let config of tableWidthConfig\" [style.width]=\"config.width\" />\r\n </colgroup>\r\n <ng-container *ngIf=\"!fixHeader\">\r\n <ng-container *ngTemplateOutlet=\"HeaderContent\"></ng-container>\r\n <ng-template *ngIf=\"!innerHeader\" [ngTemplateOutlet]=\"headerTpl\"></ng-template>\r\n </ng-container>\r\n <tbody\r\n dTableBody\r\n dSortable\r\n *ngIf=\"innerBody\"\r\n [dataSource]=\"dataSource\"\r\n [tableWidthConfig]=\"tableWidthConfig\"\r\n [rowTemplete]=\"innerBody.rowTempleteForSelect\"\r\n [minHeight]=\"minHeight\"\r\n [minHeightStretchRow]=\"innerBody.minHeightStretchRow\"\r\n ></tbody>\r\n <tbody\r\n *ngIf=\"!innerBody\"\r\n dDataTableBody\r\n #devuiNormalScrollBody\r\n [dataSource]=\"dataSource\"\r\n [resizeable]=\"resizeable\"\r\n [columns]=\"_columns\"\r\n [checkable]=\"checkable\"\r\n [allChecked]=\"_pageAllChecked\"\r\n [selectable]=\"selectable\"\r\n [editModel]=\"editModel\"\r\n [editRowItem]=\"editRowItem\"\r\n [showExpandToggle]=\"showExpandToggle\"\r\n [detailTemplateRef]=\"detailTemplateRef\"\r\n [timeout]=\"timeout\"\r\n [type]=\"type\"\r\n [rowHoveredHighlight]=\"rowHoveredHighlight\"\r\n [tableLevel]=\"tableLevel\"\r\n [tableWidthConfig]=\"tableWidthConfig\"\r\n [headerExpandConfig]=\"headerExpandConfig\"\r\n [generalRowHoveredData]=\"generalRowHoveredData\"\r\n ></tbody>\r\n </table>\r\n </div>\r\n </div>\r\n</div>\r\n\r\n<div\r\n class=\"devui-no-result-wrapper\"\r\n [ngClass]=\"{ 'overlay-table-no-result': tableOverflowType === 'overlay' || !scrollable }\"\r\n *ngIf=\"!dataSource?.length && noResultTemplate != null\"\r\n>\r\n <ng-template [ngTemplateOutlet]=\"noResultTemplate\" [ngTemplateOutletContext]=\"{ $implicit: this }\"></ng-template>\r\n</div>\r\n\r\n<ng-template #scrollViewTpl>\r\n <cdk-virtual-scroll-viewport\r\n #cdkVirtualScrollViewport\r\n dLazyLoad\r\n [enableLazyLoad]=\"lazy\"\r\n (loadMore)=\"onLoadMore($event)\"\r\n [itemSize]=\"virtualItemSize\"\r\n [minBufferPx]=\"virtualMinBufferPx\"\r\n [maxBufferPx]=\"virtualMaxBufferPx\"\r\n class=\"devui-scrollbar viewport-wrapper\"\r\n [ngClass]=\"{\r\n 'scroll-view': scrollable,\r\n 'overflow-overlay': tableOverflowType === 'overlay',\r\n 'virtual-scroll-wrap': !fixHeader\r\n }\"\r\n [style.height]=\"virtualBodyHeight\"\r\n [style.max-height]=\"maxHeight ? maxHeight : null\"\r\n [style.max-width]=\"maxWidth ? maxWidth : null\"\r\n [style.width]=\"!maxWidth ? tableWidth : null\"\r\n (scroll)=\"onBodyScroll($event)\"\r\n >\r\n <table\r\n #tableBody\r\n class=\"devui-table devui-table-{{ size }} {{ borderType ? 'table-' + borderType : '' }} {{\r\n rowHoveredHighlight ? 'table-hover' : ''\r\n }} {{ headerBg ? 'header-bg' : '' }} {{ striped ? 'table-striped' : '' }}\"\r\n [ngClass]=\"{ 'area-active': showOperationArea }\"\r\n [style.table-layout]=\"tableLayout\"\r\n [style.height]=\"minHeight ? minHeight : null\"\r\n >\r\n <colgroup>\r\n <col *ngFor=\"let config of tableWidthConfig\" [style.width]=\"config.width\" />\r\n </colgroup>\r\n <ng-container *ngIf=\"!fixHeader\">\r\n <ng-container *ngTemplateOutlet=\"HeaderContent\"></ng-container>\r\n <ng-template *ngIf=\"!innerHeader\" [ngTemplateOutlet]=\"headerTpl\"></ng-template>\r\n </ng-container>\r\n <tbody\r\n dTableBody\r\n dSortable\r\n *ngIf=\"innerBody\"\r\n [dataSource]=\"dataSource\"\r\n [tableWidthConfig]=\"tableWidthConfig\"\r\n [virtualScroll]=\"virtualScroll\"\r\n [rowTemplete]=\"innerBody.rowTempleteForSelect\"\r\n [minHeight]=\"minHeight\"\r\n [minHeightStretchRow]=\"innerBody.minHeightStretchRow\"\r\n ></tbody>\r\n <tbody\r\n *ngIf=\"!innerBody\"\r\n dDataTableBody\r\n [dataSource]=\"dataSource\"\r\n [resizeable]=\"resizeable\"\r\n [columns]=\"_columns\"\r\n [checkable]=\"checkable\"\r\n [allChecked]=\"_pageAllChecked\"\r\n [selectable]=\"selectable\"\r\n [editModel]=\"editModel\"\r\n [editRowItem]=\"editRowItem\"\r\n [showExpandToggle]=\"showExpandToggle\"\r\n [detailTemplateRef]=\"detailTemplateRef\"\r\n [timeout]=\"timeout\"\r\n [type]=\"type\"\r\n [rowHoveredHighlight]=\"rowHoveredHighlight\"\r\n [tableLevel]=\"tableLevel\"\r\n [virtualScroll]=\"virtualScroll\"\r\n [tableWidthConfig]=\"tableWidthConfig\"\r\n [headerExpandConfig]=\"headerExpandConfig\"\r\n [generalRowHoveredData]=\"generalRowHoveredData\"\r\n ></tbody>\r\n </table>\r\n </cdk-virtual-scroll-viewport>\r\n</ng-template>\r\n\r\n<ng-template #headerTpl>\r\n <thead\r\n dDataTableHead\r\n [resizeable]=\"resizeable\"\r\n [checkOptionsIndex]=\"checkOptionsIndex\"\r\n [tableViewRefElement]=\"tableViewRefElement\"\r\n [tableBodyEl]=\"tableBodyEl\"\r\n [multiSort]=\"multiSort\"\r\n [columns]=\"_columns\"\r\n [pageAllChecked]=\"_pageAllChecked\"\r\n [checkable]=\"checkable\"\r\n [checkOptions]=\"checkOptions\"\r\n [selectOptionOnCheckbox]=\"selectOptionOnCheckbox\"\r\n [headerCheckDisabled]=\"headerCheckDisabled\"\r\n [headerCheckVisible]=\"headerCheckVisible\"\r\n [showExpandToggle]=\"showExpandToggle\"\r\n [showSortIcon]=\"showSortIcon\"\r\n [showFilterIcon]=\"showFilterIcon\"\r\n (resizeHandlerEvent)=\"onResizeHandler($event)\"\r\n (headClickSortEvent)=\"onHandleSort($event)\"\r\n [halfChecked]=\"halfChecked\"\r\n [maxHeight]=\"maxHeight\"\r\n (beginResizeHandlerEvent)=\"beginResizeHandlerEvent($event)\"\r\n (resizingHandlerEvent)=\"onResizingFixedHandler($event)\"\r\n [fixHeader]=\"fixHeader\"\r\n (dragTableEndEvent)=\"handleDragTable($event)\"\r\n [dataSource]=\"dataSource\"\r\n [colDropFreezeTo]=\"colDropFreezeTo\"\r\n [colDraggable]=\"colDraggable\"\r\n [childrenTableOpen]=\"childrenTableOpen\"\r\n ></thead>\r\n</ng-template>\r\n<ng-template #HeaderContent><ng-content select=\"thead\"></ng-content></ng-template>\r\n", styles: [".devui-font-size-base{font-size:var(--devui-font-size, 12px)}.devui-font-base{font-size:var(--devui-font-size, 12px);font-weight:var(--devui-font-content-weight, normal);line-height:var(--devui-line-height-base, 1.5)}.devui-font-size-modal-title{font-size:var(--devui-font-size-modal-title, 18px)}.devui-font-modal-title{font-size:var(--devui-font-size-modal-title, 18px);font-weight:var(--devui-font-title-weight, bold);line-height:var(--devui-line-height-base, 1.5)}.devui-font-size-page-title{font-size:var(--devui-font-size-page-title, 16px)}.devui-font-page-title{font-size:var(--devui-font-size-page-title, 16px);font-weight:var(--devui-font-title-weight, bold);line-height:var(--devui-line-height-base, 1.5)}.devui-font-size-secondary-title{font-size:var(--devui-font-size-card-title, 14px)}.devui-font-secondary-title{font-size:var(--devui-font-size-card-title, 14px);font-weight:var(--devui-font-title-weight, bold);line-height:var(--devui-line-height-base, 1.5)}:host{display:block}:host.devui-table-shadow{border-radius:var(--devui-border-radius-card, 6px);box-shadow:var(--devui-shadow-length-base, 0 1px 6px 0) var(--devui-light-shadow, rgba(0, 0, 0, .08));background-color:var(--devui-base-bg, #ffffff)}:host.devui-table-shadow ::ng-deep table tbody tr:last-child td{border-bottom-color:transparent}.devui-data-table ::ng-deep{border:none;height:100%}.devui-data-table ::ng-deep .devui-table-view{padding:0;border:none;position:relative;height:100%;z-index:1}.devui-data-table ::ng-deep .devui-table-view.contain-fix-height{display:flex;flex-direction:column}.devui-data-table ::ng-deep .devui-table-view.contain-fix-height .scroll-view{flex:1}.devui-data-table ::ng-deep .devui-table-view.has-width-scroll:not(:hover):not(:has(.table-row-selected)):after{content:\"\";position:absolute;right:0;bottom:0;top:0;width:50px;z-index:100;background:linear-gradient(90deg,transparent,var(--devui-base-bg, #ffffff))}.devui-data-table ::ng-deep .devui-table-view.has-height-scroll:not(:hover):before{content:\"\";position:absolute;left:0;bottom:0;right:0;z-index:100;height:20px;background:linear-gradient(180deg,transparent,var(--devui-base-bg, #ffffff))}.devui-data-table ::ng-deep .devui-table-view .scroll-view{overflow-x:auto;overflow-y:scroll}.devui-data-table ::ng-deep .devui-table-view .scroll-view.overflow-overlay.table-wrap{overflow:auto}.devui-data-table ::ng-deep .devui-table-view .scroll-view.overflow-overlay::-webkit-scrollbar-thumb{background-color:transparent}.devui-data-table ::ng-deep .devui-table-view .scroll-view.overflow-overlay:hover::-webkit-scrollbar-thumb{background-color:var(--devui-line, #dbdbdb)}.devui-data-table ::ng-deep .devui-table-view .scroll-view.overflow-overlay:hover::-webkit-scrollbar-thumb:hover{background-color:var(--devui-placeholder, #808080)}.devui-data-table ::ng-deep .devui-table-view .devui-table{border-collapse:separate;border-spacing:0;table-layout:fixed;width:100%;max-width:100%;margin:0}.devui-data-table ::ng-deep .devui-table-view .devui-table>tbody+tbody{border-top:none}.devui-data-table ::ng-deep .devui-table-view .viewport-wrapper{overflow-x:auto;overflow-y:scroll;position:relative}.devui-data-table ::ng-deep .devui-table-view .viewport-wrapper.overflow-overlay.virtual-scroll-wrap{overflow:auto}.devui-data-table ::ng-deep .devui-table-view .viewport-wrapper.overflow-overlay::-webkit-scrollbar-thumb{background-color:transparent}.devui-data-table ::ng-deep .devui-table-view .viewport-wrapper.overflow-overlay:hover::-webkit-scrollbar-thumb{background-color:var(--devui-line, #dbdbdb)}.devui-data-table ::ng-deep .devui-table-view .viewport-wrapper.overflow-overlay:hover::-webkit-scrollbar-thumb:hover{background-color:var(--devui-placeholder, #808080)}.devui-data-table ::ng-deep .devui-table-view .viewport-wrapper.has-width-scroll:not(:hover):after{content:\"\";position:absolute;right:0;bottom:0;top:0;width:50px;background:linear-gradient(90deg,transparent,#ffffff)}.devui-data-table ::ng-deep .devui-table-view .viewport-wrapper.has-height-scroll:not(:hover):before{content:\"\";position:absolute;left:0;bottom:0;right:0;height:20px;background:linear-gradient(180deg,transparent,#ffffff)}.devui-data-table ::ng-deep .devui-table-view .resize-bar{top:0;bottom:0;position:absolute;cursor:col-resize;background:var(--devui-form-control-line-active, #0a59f7);width:2px;z-index:9999;display:none}.devui-data-table ::ng-deep .devui-table-view .resize-overlay{position:absolute;display:block;top:0;left:0;bottom:0!important;right:0;z-index:1000}.devui-data-table ::ng-deep .table-wrap{overflow-y:auto}.devui-data-table ::ng-deep .virtual-scroll-wrap .cdk-virtual-scroll-content-wrapper{padding-top:8px}.devui-table ::ng-deep thead tr{background-color:var(--devui-base-bg, #ffffff)}.devui-table ::ng-deep thead tr th{text-align:left;background-clip:padding-box!important;border-bottom:1px solid var(--devui-dividing-line, #f0f0f0);display:table-cell;padding:0;font-size:0;background-color:inherit}.devui-table ::ng-deep thead tr th .drag-icon{display:inline-block;margin-right:2px;visibility:hidden}.devui-table ::ng-deep thead tr th .drag-icon+.childtable-toggler>.customized-icon{font-size:var(--devui-font-size-icon, 16px)}.devui-table ::ng-deep thead tr th>*{display:inline-block}.devui-table ::ng-deep thead