DevUI Design中后台产品的开源前端解决方案之DataTable 表格组件核心解析

📊 DataTable 表格组件核心解析

DevUI DataTable 是一个基于 Angular 的企业级表格组件 ,支持数据展示、排序、过滤、编辑、树形结构、虚拟滚动 等多种高级功能,适用于复杂的中后台管理系统。

1. 安装与引入

在你的 Angular 项目(要求 ^18.0.0)中,需要先安装并导入 DataTable 模块。

typescript 复制代码
// app.module.ts 或相应的功能模块
import { NgModule } from '@angular/core';
import { DataTableModule } from 'ng-devui/data-table';

@NgModule({
  imports: [
    // ... 其他模块
    DataTableModule
  ],
  // ...
})
export class AppModule { }

2. 基础使用与核心参数

一个最简单的表格只需要 dataSource 数据源和列定义即可工作。

html 复制代码
<!-- your-component.component.html -->
<d-data-table #datatable [dataSource]="basicDataSource" [scrollable]="true" [tableOverflowType]="'overlay'">
  <d-column field="$index" header="#" [width]="'50px'"></d-column>
  <d-column
    *ngFor="let colOption of dataTableOptions.columns"
    [field]="colOption.field"
    [header]="colOption.header"
    [fieldType]="colOption.fieldType"
    [width]="'150px'"
  >
  </d-column>
</d-data-table>
typescript 复制代码
import { Component,OnInit } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { CommonModule } from '@angular/common';
import { PanelModule } from 'ng-devui';
import { DataTableModule } from 'ng-devui/data-table';
import { TableWidthConfig } from 'ng-devui/data-table';
import { originSource, SourceType } from './mockdata';
// import { BrowserAnimationsModule } from '@angular/platform-browser/animations';

@Component({  
  selector: 'app-root',
  standalone: true,
  imports: [CommonModule,RouterOutlet,PanelModule,DataTableModule],
  templateUrl: './app.component.html',
  styleUrl: './app.component.less'
})
export class AppComponent implements OnInit {
  title = 'devuidemo';
   // 基础数据源
  basicDataSource: Array<SourceType> = JSON.parse(JSON.stringify(originSource.slice(0, 6)));
  dataTableOptions = {
    columns: [
      {
        field: 'firstName',
        header: 'First Name',
        fieldType: 'text',
        order: 1
      },
      {
        field: 'lastName',
        header: 'Last Name',
        fieldType: 'text',
        order: 2
      },
      {
        field: 'gender',
        header: 'Gender',
        fieldType: 'text',
        order: 3
      },
      {
        field: 'dob',
        header: 'Date of birth',
        fieldType: 'date',
        order: 4
      }
    ]
  };
  ngOnInit() {
  }
}
🔑 核心配置参数速查

下表汇总了开发中最常用的一些参数,你可以根据需求快速查阅:

参数 类型 默认值 说明
dataSource any[] -- 必选,表格的数据源
size `'sm' 'md' 'lg'`等
checkable boolean -- 是否启用行选择(显示复选框)
fixHeader boolean false 是否固定表头(配合 maxHeight 使用)
maxHeight string -- 设置表格最大高度,超出则内部滚动
scrollable boolean -- 表格内容溢出容器时是否允许滚动
striped boolean false 是否显示斑马纹(交替行背景色)
borderType `'' 'bordered'`等 ''

3. 典型功能场景与代码示例

场景一:表头固定的交互表格

此场景常用于需要对行数据进行批量操作的列表。

html 复制代码
<d-data-table
  [dataSource]="maxHeightDataSource"
  [fixHeader]="true"
  [tableOverflowType]="'overlay'"
  tableHeight="360px"
  [containFixHeaderHeight]="true"
  [scrollable]="true"
>
  <thead dTableHead>
    <tr dTableRow>
      <th dHeadCell *ngFor="let colOption of dataTableOptions.columns">{{ colOption.header }}</th>
    </tr>
  </thead>
  <tbody dTableBody>
    <ng-template let-rowItem="rowItem" let-rowIndex="rowIndex">
      <tr dTableRow>
        <td dTableCell *ngFor="let colOption of dataTableOptions.columns">
          {{ colOption.fieldType === 'date' ? (rowItem[colOption.field] | i18nDate : 'short' : false) : rowItem[colOption.field] }}
        </td>
      </tr>
    </ng-template>
  </tbody>
</d-data-table>
typescript 复制代码
import {
  ChangeDetectionStrategy,
  Component,
  OnInit
} from '@angular/core';
import { originSource, SourceType } from '../mock-data';

@Component({
  selector: 'd-datatable-demo-maxheight',
  changeDetection: ChangeDetectionStrategy.OnPush,
  templateUrl: './data-table-demo-maxheight.component.html'
})
export class DatatableDemoMaxheightComponent implements OnInit {

  dataTableOptions = {
    columns: [
      {
        field: 'firstName',
        header: 'First Name',
        fieldType: 'text',
        sortable: true,
      },
      {
        field: 'lastName',
        header: 'Last Name',
        fieldType: 'text',
        sortable: true,
      },
      {
        field: 'gender',
        header: 'gender',
        fieldType: 'text',
        sortable: true,
      },
      {
        field: 'dob',
        header: 'Date of birth',
        fieldType: 'date',
        sortable: true,
      }
    ]
  };

  maxHeightDataSource: Array<SourceType> = JSON.parse(JSON.stringify(originSource.slice()));

  ngOnInit() {
  }
}
场景二:支持可编辑单元格的表格

单元格编辑是表格编辑时候的常用功能。

html 复制代码
<d-data-table #dataTable size="sm" [dataSource]="basicDataSource" [tableOverflowType]="'overlay'" [scrollable]="true">
  <thead dTableHead>
    <tr dTableRow>
      <th dHeadCell>Last Name</th>
      <th dHeadCell>Date of birth</th>
      <th dHeadCell>Age</th>
      <th dHeadCell>Gender</th>
    </tr>
  </thead>
  <tbody dTableBody>
    <ng-template let-rowItem="rowItem" let-rowIndex="rowIndex">
      <tr dTableRow>
        <td
          dTableCell
          [editable]="true"
          [editableTip]="editableTip"
          [(editing)]="rowItem['nameEdit']"
          [rowItem]="rowItem"
          [field]="'lastName'"
          [beforeEditStart]="beforeEditStart"
          [beforeEditEnd]="beforeEditEnd"
        >
          <span *ngIf="!rowItem['nameEdit']">{{ rowItem?.lastName }}</span>
          <div *ngIf="rowItem['nameEdit']" class="edit-padding-fix">
            <!-- 引入dCommonsModule的autofocus -->
            <input
              [dAutoFocus]="true"
              class="devui-form-control"
              name="lastname"
              [(ngModel)]="rowItem.lastName"
              [attr.maxlength]="100"
              [attr.minlength]="3"
            />
          </div>
        </td>
        <td dTableCell [editable]="true" [(editing)]="rowItem['dateEdit']">
          <span *ngIf="!rowItem['dateEdit']">{{ rowItem?.dob | i18nDate : 'short' : false }}</span>
          <form *ngIf="rowItem['dateEdit']" class="form-inline edit-padding-fix">
            <div class="devui-form-group">
              <div class="devui-input-group devui-dropdown-origin">
                <input
                  class="devui-form-control search"
                  name="date"
                  [(ngModel)]="rowItem.dob"
                  dDatepicker
                  appendToBody
                  #datePicker="datepicker"
                  [autoOpen]="true"
                  (ngModelChange)="onEditEnd(rowItem, 'dateEdit')"
                />
                <div class="devui-input-group-addon" (click)="datePicker.toggle()">
                  <i class="icon icon-calendar"></i>
                </div>
              </div>
            </div>
          </form>
        </td>
        <td dTableCell [editable]="true" [(editing)]="rowItem['ageEdit']">
          <span *ngIf="!rowItem['ageEdit']">{{ rowItem?.age }}</span>
          <div *ngIf="rowItem['ageEdit']" class="edit-padding-fix">
            <d-input-number [autoFocus]="true" [(ngModel)]="rowItem.age"></d-input-number>
          </div>
        </td>
        <td dTableCell [editable]="true" [(editing)]="rowItem['genderEdit']">
          <span *ngIf="!rowItem['genderEdit']">{{ rowItem?.gender?.label }}</span>
          <div *ngIf="rowItem['genderEdit']" class="customized-editor edit-padding-fix">
            <d-select
              [options]="genderSource"
              [isSearch]="true"
              [filterKey]="'label'"
              [autoFocus]="true"
              [toggleOnFocus]="true"
              [appendToBody]="true"
              [(ngModel)]="rowItem.gender"
              (ngModelChange)="onEditEnd(rowItem, 'genderEdit')"
            >
              <ng-template let-option="option" let-filterKey="filterKey"> gender:{{ option[filterKey] }} </ng-template>
            </d-select>
          </div>
        </td>
      </tr>
    </ng-template>
  </tbody>
</d-data-table>
typescript 复制代码
import { Component, OnInit } from '@angular/core';
import { EditableTip } from 'ng-devui/data-table';
import { cloneDeep } from 'lodash-es';
import { editableOriginSource, genderSource } from '../mock-data';

@Component({
  selector: 'd-editable',
  templateUrl: './data-table-demo-editable.component.html'
})
export class DatatableDemoEditableComponent implements OnInit {
  genderSource = genderSource;
  basicDataSource = cloneDeep(editableOriginSource.slice(0, 6));

  editableTip = EditableTip.hover;
  nameEditing: boolean;

  ngOnInit() {
  }

  onEditEnd(rowItem, field) {
    rowItem[field] = false;
  }

  beforeEditStart = (rowItem, field) => {
    return true;
  };

  beforeEditEnd = (rowItem, field) => {
    console.log('beforeEditEnd');
    if (rowItem && rowItem[field].length < 3) {
      return false;
    } else {
      return true;
    }
  };
}
场景三:可展开的树形表格

用于展示具有层级关系的数据结构。

html 复制代码
<d-button class="demo-margin" (btnClick)="setUnCheckableRelation('downward')">选中父不再选中子</d-button>
<d-button bsStyle="common" class="demo-margin" (btnClick)="setUnCheckableRelation('upward')">选中子不再选中父</d-button>
<d-button bsStyle="common" class="demo-margin" (btnClick)="toggleIcon()">使用自定义展开/收起图标</d-button>
<d-button bsStyle="common" class="demo-margin" (btnClick)="expandAll()">展开全部表格</d-button>
<d-data-table
  [dataSource]="basicDataSource"
  [tableWidthConfig]="tableWidthConfig"
  [checkableRelation]="checkableRelation"
  [loadChildrenTable]="loadChildrenTable"
  [loadAllChildrenTable]="loadAllChildrenTable"
  [scrollable]="true"
  [tableOverflowType]="'overlay'"
>
  <thead dTableHead [checkable]="true">
    <tr dTableRow>
      <th dHeadCell [nestedColumn]="true" [iconFoldTable]="iconParentOpen" [iconUnFoldTable]="iconParentClose">title</th>
      <th dHeadCell>name</th>
      <th dHeadCell>status</th>
      <th dHeadCell>date</th>
    </tr>
  </thead>
  <tbody dTableBody>
    <ng-template let-rowItem="rowItem" let-rowIndex="rowIndex" let-nestedLayer="nestedLayer" let-nestedIndex="nestedIndex">
      <tr dTableRow [ngClass]="{ 'table-row-selected': rowItem.$checked }">
        <td dTableCell class="devui-checkable-cell">
          <d-checkbox
            [ngModelOptions]="{ standalone: true }"
            [ngModel]="rowItem.$checked"
            [halfchecked]="rowItem.$halfChecked"
            [disabled]="rowItem.$checkDisabled"
            (ngModelChange)="onRowCheckChange($event, rowIndex, nestedIndex, rowItem)"
            dTooltip
            [content]="rowItem.$checkBoxTips"
            [position]="['top', 'right', 'bottom', 'left']"
          >
          </d-checkbox>
        </td>
        <td
          dTableCell
          [nestedColumn]="true"
          [nestedColumnIndent]="20"
          [rowItem]="rowItem"
          [nestedLayer]="nestedLayer"
          [iconFoldTable]="iconParentOpen"
          [iconUnFoldTable]="iconParentClose"
          (toggleChildTableEvent)="onChildTableToggle($event, rowItem)"
        >
          {{ rowItem['title'] }}
        </td>
        <td dTableCell>{{ rowItem['lastName'] }}</td>
        <td dTableCell>{{ rowItem['status'] }}</td>
        <td dTableCell>{{ rowItem['dob'] | i18nDate : 'short' : false }}</td>
      </tr>
    </ng-template>
  </tbody>
</d-data-table>
typescript 复制代码
import {
  Component,
  OnInit,
  ViewChild
} from '@angular/core';
import {
  CheckableRelation,
  DataTableComponent,
  TableWidthConfig
} from 'ng-devui/data-table';
import {
  SourceType,
  treeDataSource
} from '../mock-data';

@Component({
  selector: 'd-tree-data',
  templateUrl: './tree-data.component.html',
  styles: ['.demo-margin { margin: 5px 5px 0 0;}']
})
export class TreeDataComponent implements OnInit {
  iconParentOpen: string;
  iconParentClose: string;
  basicDataSource: Array<SourceType> = JSON.parse(JSON.stringify(treeDataSource.slice(0, 6)));
  checkableRelation: CheckableRelation = {downward: true, upward: true};
  @ViewChild(DataTableComponent, { static: true }) datatable: DataTableComponent;

  tableWidthConfig: TableWidthConfig[] = [
    {
      field: 'checkbox',
      width: '4%'
    },
    {
      field: 'title',
      width: '36%'
    },
    {
      field: 'lastName',
      width: '20%'
    },
    {
      field: 'status',
      width: '20%'
    },
    {
      field: 'dob',
      width: '20%'
    }
  ];

  ngOnInit() {
    this.basicDataSource[0].$isChildTableOpen = true;
  }

  onChildTableToggle(status, rowItem) {
    this.datatable.setRowChildToggleStatus(rowItem, status);
  }

  loadChildrenTable = (rowItem) => {
    return new Promise((resolve) => {
      setTimeout(() => {
        if (rowItem.title === 'table title1') {
          if (rowItem.children && rowItem.children.length === 0) {
            rowItem.children.push({
              title: 'table title11',
              lastName: 'Mark',
              status: 'done',
              dob: new Date(1989, 1, 1),
              startDate: new Date(2020, 1, 4),
              endDate: new Date(2020, 1, 8)
            });
          }
        }
        resolve(rowItem);
      }, 500);

    });
  };

  loadAllChildrenTable = () => {
    return new Promise((resolve) => {
      setTimeout(() => {
        this.basicDataSource[0].children[0].children[1].children[0].children = [];
        this.basicDataSource[0].children[0].children[1].children[0].children.push({
          title: 'table title01211',
          lastName: 'Mark',
          status: 'done',
          dob: new Date(1989, 1, 1),
        },
        {
          title: 'table title01212',
          lastName: 'Mark',
          status: 'done',
          dob: new Date(1991, 3, 1)
        });
        resolve(undefined);
      }, 500);
    });
  };

  setUnCheckableRelation(type) {
    if (type === 'upward') {
      this.checkableRelation.upward = false;
    } else {
      this.checkableRelation.downward = false;
    }
  }

  toggleIcon() {
    this.iconParentOpen = '<span class="icon icon-chevron-right"></span>';
    this.iconParentClose = '<span class="icon icon-chevron-down"></span>';
  }

  onRowCheckChange(checked, rowIndex, nestedIndex, rowItem) {
    rowItem.$checked = checked;
    rowItem.$halfChecked = false;
    this.datatable.setRowCheckStatus({
      rowIndex: rowIndex,
      nestedIndex: nestedIndex,
      rowItem: rowItem,
      checked: checked
    });
  }

  expandAll() {
    this.datatable.setTableChildrenToggleStatus(true);
  }
}

4. 开发实践与建议

  1. 性能优化 :当渲染超大数据集 (如数千行)时,务必开启 virtualScroll(虚拟滚动) 功能,它只会渲染可视区域内的 DOM 元素,能极大提升性能。
  2. 状态管理 :如果表格与复杂状态关联(如多步骤表单、全局状态),建议将 dataSource前端状态管理(如 NgRx、Akita)结合,而不是依赖组件内部状态。
  3. 列配置 :对于动态列的需求,可以创建一个 Column[] 配置数组,配合 *ngFor 在模板中动态生成 <d-table-column>,这样更加灵活。
  4. 数据更新 :修改 dataSource 数组引用(例如 this.dataSource = [...newData])会触发表格完整刷新 。若只想更新某一行,可以修改该行对象的属性并配合 ChangeDetectorRefdetectChanges() 方法。

参考资料:

MateChat:https://gitcode.com/DevCloudFE/MateChat

MateChat官网:https://matechat.gitcode.com

DevUI官网:https://devui.design/home

相关推荐
黑臂麒麟2 小时前
DevUI modal 弹窗表单联动实战:表格编辑功能完整实现
前端·javascript·ui·angular.js
懒人村杂货铺2 小时前
FastAPI + 前端(Vue/React)Docker 部署全流程
前端·vue.js·fastapi
7***37452 小时前
前端技术的下一站:从“页面开发”走向“体验工程”
前端
哆啦A梦15882 小时前
商城后台管理系统 01,商品管理-搜索
前端·javascript·vue.js
苏小瀚2 小时前
[JavaEE] Spring Web MVC入门
前端·java-ee·mvc
前端不太难2 小时前
RN 构建包体积过大,如何瘦身?
前端·react native
小光学长2 小时前
基于web的影视网站设计与实现14yj533o(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
java·前端·数据库
vocoWone2 小时前
📰 前端资讯 - 2025年12月10日
前端
李少兄2 小时前
前端开发中的多列布局(Multi-column Layout)
前端·css