# DataTable 数据表格组件

一个功能强大且灵活的数据表格组件，支持排序、行选择、内联扩展、自定义单元格格式、操作列、冻结列和行操作。非常适合显示大型数据集、复杂数据结构和用户交互。

## 安装

```bash
npm install @ticatec/uniface-element
```

## 导入

```typescript
import DataTable from "@ticatec/uniface-element/DataTable";
import type { DataColumn, ActionsColumn, IndicatorColumn, RowAction } from "@ticatec/uniface-element/DataTable";
```

## 基本用法

```svelte
<script>
  import DataTable from "@ticatec/uniface-element/DataTable";
  
  const columns = [
    { text: "姓名", field: "name", width: 150 },
    { text: "年龄", field: "age", width: 80, align: "center" },
    { text: "电子邮件", field: "email", width: 200 }
  ];
  
  const list = [
    { name: "张伟", age: 30, email: "zhangwei@example.com" },
    { name: "李娜", age: 25, email: "lina@example.com" }
  ];
</script>

<DataTable {columns} {list} />
```

## 属性

| 属性 | 类型 | 默认值 | 描述 |
|------|------|---------|-------------|
| `columns` | `Array<DataColumn>` | 必需 | 列配置数组 |
| `list` | `Array<any>` | 必需 | 要显示的数据数组 |
| `actionsColumn` | `ActionsColumn \| null` | `null` | 操作列配置 |
| `indicatorColumn` | `IndicatorColumn \| null` | `null` | 指示器/选择列配置 |
| `selectedRows` | `Array<any>` | `[]` | 选中行的数据数组 |
| `rowHeight` | `number` | `42` | 常规行高度（像素） |
| `inlineRowHeight` | `number` | `80` | 扩展内联行高度（像素） |
| `rowBorder` | `boolean` | `false` | 是否显示水平行边框 |
| `colBorder` | `boolean` | `false` | 是否显示垂直列边框 |
| `emptyIndicator` | `string \| undefined` | `undefined` | 无数据时显示的文本 |
| `style` | `string` | `""` | 附加 CSS 样式 |

## 类型

### DataColumn

```typescript
interface DataColumn {
  text: string;                    // 列标题文本
  field?: string;                  // 数据字段名称
  width: number;                   // 列宽（像素）
  frozen?: boolean;                // 列是否冻结/固定
  align?: 'left' | 'center' | 'right';  // 文本对齐方式
  minWidth?: number;               // 最小列宽
  warp?: boolean;                  // 文本是否可换行
  formatter?: FormatCell;          // 单元格格式化函数
  escapeHTML?: boolean;            // 是否转义 HTML 内容
  href?: HrefBuilder;              // 链接生成函数
  hint?: CellHint;                 // 工具提示/提示函数
  render?: any;                    // 自定义 Svelte 组件渲染器
  visible?: boolean;               // 列可见性
  resizable?: boolean;             // 列是否可调整大小
  compareFunction?: CompareFunction;  // 排序比较函数
}
```

### ActionsColumn

```typescript
interface ActionsColumn {
  width: number;                   // 列宽（像素）
  getActions: GetRowActions;       // 返回每行操作的函数
  align?: 'left' | 'center';      // 操作对齐方式
}
```

### IndicatorColumn

```typescript
interface IndicatorColumn {
  width: number;                   // 列宽（像素）
  selectable?: boolean;            // 行是否可选择
  displayNo?: boolean;             // 是否显示行号
  buildInlineComponent?: (data: any) => Promise<any>;  // 内联组件生成器
}
```

### RowAction

```typescript
interface RowAction {
  label: string;                   // 操作按钮文本
  icon?: string;                   // 可选图标名称
  type?: "primary" | "secondary";  // 按钮类型/样式
  callback: ActionFunction;        // 点击处理函数
}
```

## 示例

### 基本数据表格

```svelte
<script>
  import DataTable from "@ticatec/uniface-element/DataTable";
  
  const columns = [
    {
      text: "产品名称",
      field: "name",
      width: 200,
      resizable: true
    },
    {
      text: "价格",
      field: "price",
      width: 100,
      align: "right",
      formatter: (value) => `¥${value?.toFixed(2) ?? '0.00'}`
    },
    {
      text: "类别",
      field: "category",
      width: 150,
      align: "center"
    },
    {
      text: "库存",
      field: "stock",
      width: 80,
      align: "center",
      compareFunction: (a, b) => (a?.stock ?? 0) - (b?.stock ?? 0)
    }
  ];
  
  const products = [
    { name: "笔记本电脑", price: 6999.99, category: "电子产品", stock: 15 },
    { name: "智能手机", price: 4999.99, category: "电子产品", stock: 25 },
    { name: "书籍", price: 99.99, category: "教育", stock: 100 },
    { name: "耳机", price: 999.99, category: "电子产品", stock: 8 }
  ];
</script>

<div class="table-container">
  <DataTable columns={columns} list={products} rowBorder={true} />
</div>

<style>
  .table-container {
    width: 100%;
    height: 400px;
    border: 1px solid #e2e8f0;
    border-radius: 8px;
  }
</style>
```

### 带有行选择和操作的表格

```svelte
<script>
  import DataTable from "@ticatec/uniface-element/DataTable";
  
  const columns = [
    { text: "姓名", field: "name", width: 150 },
    { text: "电子邮件", field: "email", width: 200 },
    { text: "角色", field: "role", width: 120 },
    { text: "状态", field: "status", width: 100, align: "center",
      formatter: (value) => {
        const colors = { active: 'green', inactive: 'red', pending: 'orange' };
        return `<span style="color: ${colors[value] || 'black'}">${value}</span>`;
      },
      escapeHTML: true
    }
  ];
  
  const indicatorColumn = {
    width: 60,
    selectable: true,
    displayNo: true
  };
  
  const actionsColumn = {
    width: 120,
    align: 'center',
    getActions: (user) => [
      {
        label: '编辑',
        icon: 'icon_google_create',
        callback: () => handleEdit(user)
      },
      {
        label: '删除',
        icon: 'icon_google_delete',
        type: 'secondary',
        callback: () => handleDelete(user)
      }
    ]
  };
  
  const users = [
    { name: "张丽", email: "zhangli@company.com", role: "管理员", status: "active" },
    { name: "王强", email: "wangqiang@company.com", role: "用户", status: "active" },
    { name: "李芳", email: "lifang@company.com", role: "经理", status: "pending" }
  ];
  
  let selectedRows = [];
  
  function handleEdit(user) {
    console.log('正在编辑用户:', user);
  }
  
  function handleDelete(user) {
    console.log('正在删除用户:', user);
  }
  
  $: console.log('已选择用户:', selectedRows);
</script>

<DataTable 
  {columns} 
  list={users} 
  {indicatorColumn} 
  {actionsColumn}
  bind:selectedRows
  rowBorder={true}
  colBorder={true}
/>
```

### 带有自定义单元格渲染和排序的表格

```svelte
<script>
  import DataTable from "@ticatec/uniface-element/DataTable";
  import UserAvatar from "./UserAvatar.svelte";
  
  const columns = [
    {
      text: "用户",
      field: "user",
      width: 200,
      render: UserAvatar
    },
    {
      text: "分数",
      field: "score",
      width: 100,
      align: "center",
      formatter: (value) => `${value}/100`,
      compareFunction: (a, b) => (a?.score ?? 0) - (b?.score ?? 0)
    },
    {
      text: "最后登录",
      field: "lastLogin",
      width: 150,
      compareFunction: (a, b) => {
        const dateA = new Date(a?.lastLogin ?? 0);
        const dateB = new Date(b?.lastLogin ?? 0);
        return dateA.getTime() - dateB.getTime();
      },
      formatter: (value) => {
        return value ? new Date(value).toLocaleDateString('zh-CN') : '从未登录';
      }
    },
    {
      text: "操作",
      field: "website",
      width: 120,
      href: (item) => [{
        text: "访问个人资料",
        action: () => window.open(`/profile/${item.id}`, '_blank')
      }, {
        text: "发送消息",
        action: () => handleSendMessage(item)
      }]
    }
  ];
  
  const userData = [
    { 
      id: 1, 
      user: { name: "张伟", avatar: "/avatars/zhangwei.jpg" },
      score: 85,
      lastLogin: "2024-01-15",
      website: "zhangwei-portfolio.com"
    },
    { 
      id: 2, 
      user: { name: "李娜", avatar: "/avatars/lina.jpg" },
      score: 92,
      lastLogin: "2024-01-10",
      website: "lina-designs.com"
    }
  ];
  
  function handleSendMessage(user) {
    console.log('向用户发送消息:', user.user.name);
  }
</script>

<DataTable {columns} list={userData} rowHeight={50} />
```

### 带有内联行扩展的表格

```svelte
<script>
  import DataTable from "@ticatec/uniface-element/DataTable";
  import OrderDetails from "./OrderDetails.svelte";
  
  const columns = [
    { text: "订单ID", field: "orderId", width: 120 },
    { text: "客户", field: "customer", width: 150 },
    { text: "总计", field: "total", width: 100, align: "right",
      formatter: (value) => `¥${value?.toFixed(2) ?? '0.00'}`
    },
    { text: "状态", field: "status", width: 120, align: "center" },
    { text: "日期", field: "orderDate", width: 120 }
  ];
  
  const indicatorColumn = {
    width: 50,
    selectable: false,
    displayNo: false,
    buildInlineComponent: async (orderData) => {
      // 模拟 API 调用以获取订单详情
      await new Promise(resolve => setTimeout(resolve, 1000));
      return OrderDetails;
    }
  };
  
  const orders = [
    {
      orderId: "ORD-001",
      customer: "张伟",
      total: 2999.99,
      status: "已发货",
      orderDate: "2024-01-15",
      items: [
        { product: "笔记本电脑", quantity: 1, price: 2999.99 }
      ]
    },
    {
      orderId: "ORD-002", 
      customer: "李娜",
      total: 1499.98,
      status: "处理中",
      orderDate: "2024-01-14",
      items: [
        { product: "鼠标", quantity: 2, price: 249.99 },
        { product: "键盘", quantity: 1, price: 999.99 }
      ]
    }
  ];
</script>

<DataTable 
  {columns} 
  list={orders} 
  {indicatorColumn}
  inlineRowHeight={200}
  emptyIndicator="未找到订单"
/>
```

### 带有冻结列的表格

```svelte
<script>
  import DataTable from "@ticatec/uniface-element/DataTable";
  
  const columns = [
    {
      text: "姓名",
      field: "name",
      width: 150,
      frozen: true,
      resizable: false
    },
    {
      text: "员工ID",
      field: "employeeId",
      width: 100,
      frozen: true,
      align: "center"
    },
    { text: "部门", field: "department", width: 150 },
    { text: "职位", field: "position", width: 180 },
    { text: "薪资", field: "salary", width: 120, align: "right",
      formatter: (value) => `¥${value?.toLocaleString() ?? '0'}` }
    },
    { text: "入职日期", field: "startDate", width: 120 },
    { text: "经理", field: "manager", width: 150 },
    { text: "地点", field: "location", width: 200 },
    { text: "电话", field: "phone", width: 140 },
    { text: "电子邮件", field: "email", width: 200 }
  ];
  
  const employees = [
    {
      name: "张伟",
      employeeId: "EMP001",
      department: "工程部",
      position: "高级开发人员",
      salary: 95000,
      startDate: "2022-03-15",
      manager: "李芳",
      location: "上海",
      phone: "555-0123",
      email: "zhangwei@company.com"
    },
    // ... 更多员工记录
  ];
</script>

<div style="width: 800px; height: 400px;">
  <DataTable {columns} list={employees} />
</div>
```

### 高级表格（包含所有功能）

```svelte
<script>
  import DataTable from "@ticatec/uniface-element/DataTable";
  import ProductDetails from "./ProductDetails.svelte";
  import ProductImage from "./ProductImage.svelte";
  
  const columns = [
    {
      text: "图片",
      field: "image",
      width: 80,
      frozen: true,
      render: ProductImage,
      align: "center"
    },
    {
      text: "产品",
      field: "name",
      width: 200,
      frozen: true,
      resizable: true,
      hint: (item) => `SKU: ${item.sku}`,
      compareFunction: (a, b) => (a?.name ?? '').localeCompare(b?.name ?? '')
    },
    {
      text: "价格",
      field: "price",
      width: 100,
      align: "right",
      formatter: (value) => `¥${value?.toFixed(2) ?? '0.00'}`,
      compareFunction: (a, b) => (a?.price ?? 0) - (b?.price ?? 0)
    },
    {
      text: "库存",
      field: "stock",
      width: 80,
      align: "center",
      formatter: (value) => {
        if (value === 0) return '<span style="color: red">缺货</span>';
        if (value < 10) return `<span style="color: orange">${value}</span>`;
        return `<span style="color: green">${value}</span>`;
      },
      escapeHTML: true,
      compareFunction: (a, b) => (a?.stock ?? 0) - (b?.stock ?? 0)
    },
    {
      text: "类别",
      field: "category",
      width: 120,
      compareFunction: (a, b) => (a?.category ?? '').localeCompare(b?.category ?? '')
    },
    {
      text: "供应商",
      field: "supplier",
      width: 150,
      href: (item) => [{
        text: "联系供应商",
        action: () => openSupplierContact(item.supplierId)
      }]
    },
    {
      text: "最后更新",
      field: "updatedAt",
      width: 140,
      formatter: (value) => new Date(value).toLocaleDateString('zh-CN'),
      compareFunction: (a, b) => {
        const dateA = new Date(a?.updatedAt ?? 0);
        const dateB = new Date(b?.updatedAt ?? 0);
        return dateA.getTime() - dateB.getTime();
      }
    }
  ];
  
  const indicatorColumn = {
    width: 60,
    selectable: true,
    displayNo: true,
    buildInlineComponent: async (product) => {
      // 模拟加载产品详情
      return ProductDetails;
    }
  };
  
  const actionsColumn = {
    width: 140,
    align: 'center',
    getActions: (product) => {
      const actions = [
        {
          label: '编辑',
          icon: 'icon_google_create',
          callback: () => editProduct(product)
        },
        {
          label: '复制',
          icon: 'icon_google_content_copy',
          callback: () => cloneProduct(product)
        }
      ];
      
      if (product.stock === 0) {
        actions.push({
          label: '补货',
          icon: 'icon_google_add_shopping_cart',
          type: 'primary',
          callback: () => restockProduct(product)
        });
      }
      
      actions.push({
        label: '删除',
        icon: 'icon_google_delete',
        type: 'secondary',
        callback: () => deleteProduct(product)
      });
      
      return actions;
    }
  };
  
  let products = [
    {
      id: 1,
      name: "无线耳机",
      sku: "WH-001",
      price: 1999.99,
      stock: 25,
      category: "电子产品",
      supplier: "科技公司",
      supplierId: "SUP001",
      image: "/images/headphones.jpg",
      updatedAt: "2024-01-15T10:30:00Z"
    },
    // ... 更多产品
  ];
  
  let selectedRows = [];
  
  function editProduct(product) {
    console.log('正在编辑产品:', product);
  }
  
  function cloneProduct(product) {
    console.log('正在复制产品:', product);
  }
  
  function restockProduct(product) {
    console.log('正在补货产品:', product);
  }
  
  function deleteProduct(product) {
    console.log('正在删除产品:', product);
  }
  
  function openSupplierContact(supplierId) {
    console.log('打开供应商联系方式:', supplierId);
  }
  
  $: console.log('已选择产品:', selectedRows);
</script>

<div class="advanced-table">
  <h2>产品库存管理</h2>
  <div class="table-wrapper">
    <DataTable 
      {columns} 
      list={products}
      {indicatorColumn}
      {actionsColumn}
      bind:selectedRows
      rowHeight={60}
      inlineRowHeight={300}
      rowBorder={true}
      colBorder={true}
      emptyIndicator="未找到产品，请添加一些产品以开始。"
    />
  </div>
  
  {#if selectedRows.length > 0}
    <div class="selection-info">
      <p>已选择 {selectedRows.length} 个产品</p>
      <button on:click={() => console.log('批量编辑:', selectedRows)}>
        批量编辑
      </button>
      <button on:click={() => console.log('批量删除:', selectedRows)}>
        批量删除
      </button>
    </div>
  {/if}
</div>

<style>
  .advanced-table {
    padding: 20px;
    height: 100vh;
    display: flex;
    flex-direction: column;
  }
  
  .table-wrapper {
    flex: 1;
    border: 1px solid #e2e8f0;
    border-radius: 8px;
    overflow: hidden;
  }
  
  .selection-info {
    margin-top: 16px;
    padding: 12px;
    background: #f8fafc;
    border-radius: 6px;
    display: flex;
    align-items: center;
    gap: 12px;
  }
  
  .selection-info button {
    padding: 6px 12px;
    border: 1px solid #d1d5db;
    border-radius: 4px;
    background: white;
    cursor: pointer;
  }
  
  .selection-info button:hover {
    background: #f3f4f6;
  }
</style>
```

## 样式

DataTable 组件通过 CSS 自定义属性和类支持广泛的样式定制：

```css
.uniface-data-table {
  --table-header-bg: #f8fafc;
  --table-header-text: #374151;
  --table-row-hover: #f9fafb;
  --table-border-color: #e5e7eb;
  --table-selected-bg: #dbeafe;
}

/* 自定义行样式 */
.uniface-data-table.row-border .table-row {
  border-bottom: 1px solid var(--table-border-color);
}

/* 自定义列样式 */
.uniface-data-table.cell-border .table-cell {
  border-right: 1px solid var(--table-border-color);
}
```

## 可访问性

DataTable 组件包括以下可访问性功能：

- 支持键盘导航
- 为屏幕阅读器提供 ARIA 标签
- 交互元素的焦点管理
- 语义化表格结构
- 行选择提示

## 最佳实践

1. **性能**：对于大型数据集（1000+ 行），考虑实现虚拟滚动或分页
2. **列宽**：根据内容设置合适的宽度以防止布局偏移
3. **冻结列**：谨慎使用冻结列，仅用于关键标识符
4. **操作**：将行操作限制在 3-5 个最重要的操作
5. **加载状态**：始终为异步操作提供加载指示器
6. **错误处理**：为内联组件加载失败实现适当的错误处理
7. **响应式设计**：在较小的屏幕上考虑隐藏次要列

## 许可证

MIT