REAL SHOWCASE SOURCEdata-grid-virtualization

2 个文件 · 150 行 · TypeScript

// ===== showcase/src/examples/data_grid/virtualization.ts ===== import { createGridExample } from './shared' export function createVirtualizationGridExample() {  return createGridExample({    title: '十万行虚拟滚动',    description: '只为可见行创建绘制工作,验证高密度数据场景下的持续滚动响应。',    rowCount: 100000,    statusText: '100,000 行已加载 · 当前仅绘制可视区域 · 尝试快速拖动滚动条',  })} // ===== showcase/src/examples/data_grid/shared.ts ===== import {  RenderButton,  RenderDataGrid,  RenderPageHeader,  RenderSearchBox,  RenderStackPanel,  RenderText,  type GridColumnDef,} from 'ds-ui' export interface DemoRow {  id: number  project: string  owner: string  department: string  status: 'active' | 'review' | 'paused'  progress: number  budget: number  updatedAt: string} const owners = ['林乔', '周鸣', '陈溪', '顾宁', '唐远', '许安']const departments = ['医疗产品', '数据平台', '企业应用', '基础架构']const statusOptions = [  { value: 'active', label: '进行中' },  { value: 'review', label: '待验收' },  { value: 'paused', label: '已暂停' },] export function createRows(count: number): DemoRow[] {  return Array.from({ length: count }, (_, index) => ({    id: index + 1,    project: `业务项目 ${String(index + 1).padStart(5, '0')}`,    owner: owners[index % owners.length]!,    department: departments[index % departments.length]!,    status: (['active', 'review', 'paused'] as const)[index % 3]!,    progress: (index * 13) % 101,    budget: 80000 + (index % 48) * 12500,    updatedAt: `2026-${String((index % 12) + 1).padStart(2, '0')}-${String((index % 28) + 1).padStart(2, '0')}`,  }))} export const standardColumns: GridColumnDef<DemoRow>[] = [  { type: 'number', key: 'id', title: '编号', width: 76, pinned: 'left', align: 'center', sortable: true },  { type: 'text', key: 'project', title: '项目名称', width: 220, pinned: 'left', editable: true, filterable: true, sortable: true },  { type: 'text', key: 'owner', title: '负责人', width: 110, editable: true, filterable: true, sortable: true },  { type: 'text', key: 'department', title: '所属部门', width: 140, filterable: true, sortable: true },  { type: 'select', key: 'status', title: '状态', width: 110, editable: true, options: statusOptions, filterable: true, sortable: true },  { type: 'number', key: 'progress', title: '进度 %', width: 100, editable: true, min: 0, max: 100, align: 'right', filterable: true, sortable: true },  { type: 'number', key: 'budget', title: '预算', width: 130, editable: true, min: 0, align: 'right', filterable: true, sortable: true },  { type: 'date', key: 'updatedAt', title: '最近更新', width: 132, editable: true, filterable: true, sortable: true },] interface GridExampleOptions {  title: string  description: string  rowCount: number  editable?: boolean  grouped?: boolean  validationActions?: boolean  statusText: string} export function createGridExample(options: GridExampleOptions): RenderStackPanel {  const status = new RenderText(options.statusText)  const grid = new RenderDataGrid<DemoRow>({    rowKey: 'id',    columns: standardColumns,    rows: createRows(options.rowCount),    editable: options.editable ?? false,    sortable: true,    reorderableColumns: true,    resizableColumns: true,    autoFitColumns: true,    selectionMode: 'range',    summary: [      { key: 'id', aggregate: 'count', label: '项目数' },      { key: 'budget', aggregate: 'sum', label: '总预算' },      { key: 'progress', aggregate: 'avg', label: '平均进度' },    ],    validateCell: ({ key, value }) => {      if (key === 'project' && !String(value ?? '').trim()) return '项目名称不能为空'      if (key === 'progress' && (Number(value) < 0 || Number(value) > 100)) return '进度应在 0 到 100 之间'      return true    },    validateRow: ({ row }) => {      if (row.status === 'active' && row.progress === 0) {        return { progress: '进行中项目的进度必须大于 0' }      }      return undefined    },    onSelectionChange: event => {      status.text = `${event.state.selectedRows.length || 1} 条已选择 · 可继续拖动扩展范围`    },    onCellChange: (_row, key) => {      status.text = `已保存 ${String(key)} 字段 · 仅刷新当前单元格`    },  })  if (options.grouped) grid.groupBy = [{ key: 'status', order: 'asc' }]   const search = new RenderSearchBox({    placeholder: '搜索项目、负责人或部门…',    onChange: value => grid.setQuickFilter(value),    onSearch: value => grid.setQuickFilter(value),  })  const content = new RenderStackPanel({ orientation: 'vertical', spacing: 10, crossAxisAlignment: 'stretch' })  content.addChild(new RenderPageHeader({ title: options.title, description: options.description }))  content.addChild(search)  if (options.validationActions) {    const actions = new RenderStackPanel({ orientation: 'horizontal', spacing: 8 })    actions.addChild(new RenderButton({      label: '校验全部数据',      variant: 'primary',      onClick: () => {        const result = grid.validate()        if (!result.valid) grid.focusFirstError()        status.text = result.valid          ? '全部数据校验通过'          : `发现 ${result.errors.length} 个输入错误,已定位到第一个可见错误`      },    }))    actions.addChild(new RenderButton({      label: '清除校验结果',      onClick: () => {        grid.clearCellErrors()        status.text = options.statusText      },    }))    content.addChild(actions)  }  content.addChild(grid, 1)  content.addChild(status)  content.padding = 16  return content}