# el-excel-table

基于 **Vue 2** + **Element-UI** 的类 Excel 表格组件，内置公式计算、同值合并、分组合计、固定行与导出能力

## 功能特性

- **公式列**：`col.formula` 支持表达式计算，并按依赖顺序执行
- **同值合并**：`col.merge: true` 自动计算纵向合并范围
- **分组合计（竖向虚拟列）**：`type: 'virtual' + direction: 'vertical' + anchor + formula` 基于合并分组回填汇总值
- **可编辑占位交互**：`col.editable` + `cell-click` 事件 + `done(updates)` 回填数据后自动重算
- **固定行**：配合 `FixedRowPlugin`，支持顶部/底部 sticky 行
- **导出**：`utils.exportToExcel` 导出 Excel；`utils.exportToPdf` 导出 PDF（依赖全局 jsPDF）

## 安装

该包依赖：

- `vue@^2.6`
- `element-ui@^2.15`

```bash
npm i el-excel-table element-ui
```

或

```bash
yarn add el-excel-table element-ui
```

## 快速开始（Vue.use 安装）

`ExcelTable` 以插件方式注册组件，安装时 `engines/utils/mixins` 都可不传（默认内置）

```js
import Vue from 'vue'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'

import ExcelTable from 'el-excel-table'

Vue.use(ElementUI)

Vue.use(ExcelTable, {
  // 可选：组件名，默认 ElExcelTable
  name: 'ElExcelTable'
})
```

## 样式引入

业务项目按需引入样式：

```js
import 'el-excel-table/css/index.css'
// 或单独引入：
// import 'el-excel-table/css/excel-table.css'
// import 'el-excel-table/css/fixed-row.css'
// import 'el-excel-table/css/formula-header.css'
```

## 覆盖引擎（可选）

业务侧可以 `extends` 默认引擎并覆盖指定实现，然后通过 `Vue.use` 传入覆盖项：

```js
import ExcelTable, { FormulaEngine } from 'el-excel-table'

class MyFormulaEngine extends FormulaEngine {
  // 自定义覆盖
}

Vue.use(ExcelTable, {
  name: 'ElExcelTable',
  engines: {
    formula: MyFormulaEngine
  }
})
```

## 基本用法

```vue
<template>
  <ElExcelTable
    ref="table"
    :columns="columns"
    :data="rows"
    :height="520"
    :summary="true"
    summary-text="总计"
    edittext="编辑"
    :fixed-row="fixed_row"
    @cell-click="onCellClick"
  />
</template>

<script>
import { utils } from 'el-excel-table'

export default {
  data() {
    return {
      columns: [
        { prop: 'group', label: '分组', width: 120, merge: true },
        { prop: 'a', label: 'A', width: 120, editable: true },
        { prop: 'b', label: 'B', width: 120 },
        { prop: 'c', label: 'A+B', width: 120, formula: 'a + b', digits: 2 },
        // 竖向虚拟列：基于 anchor(分组列) 的合并信息做分组汇总，并回填到每一行
        {
          prop: 'sum_c',
          label: '分组汇总',
          width: 120,
          type: 'virtual',
          direction: 'vertical',
          anchor: 'group',
          formula: 'c'
        }
      ],
      rows: [
        { group: 'G1', a: 1, b: 2 },
        { group: 'G1', a: 3, b: 4 },
        { group: 'G2', a: 10, b: 1 }
      ]
    }
  },
  methods: {
    fixed_row(row, rowIndex) {
      // true / 'top' => 顶部固定；'bottom' => 底部固定
      if (rowIndex === 0) return 'top'
      return false
    },
    onCellClick({ row, column, rowIndex, columnIndex, done }) {
      // 仅当该列 editable 且单元格为空时触发
      // done 接收 { [prop]: value }，会回填到原始 data 并触发重算
      done({ a: 100 })
    },
    async export_excel() {
      const { data, columns } = this.$refs.table.getExportData()
      // 业务侧自行安装并注入 XLSX，一次注入后直接调用即可
      // import * as XLSX from 'xlsx'
      const export_excel = utils.createExcelExporter(XLSX)
      await export_excel({ data, columns, filename: 'demo.xlsx', sheetName: '报表数据' })
    }
  }
}
</script>
```

## 组件 Props

| 参数          | 说明                                                   | 类型                                            | 必填 | 默认值    |
| ------------- | ------------------------------------------------------ | ----------------------------------------------- | ---- | --------- |
| columns       | 列配置                                                 | `ColumnConfig[]`                                | 是   | -         |
| data          | 表格数据                                               | `TableRow[]`                                    | 否   | `[]`      |
| height        | 表格高度                                               | `number \| string`                              | 是   | -         |
| summary       | 是否显示总计行                                         | `boolean`                                       | 否   | `true`    |
| summaryText   | 总计首列文案                                           | `string`                                        | 否   | `总计`    |
| edittext      | 可编辑空值占位文案                                     | `string`                                        | 否   | `编辑`    |
| editableColor | 可编辑列文字颜色                                       | `string`                                        | 否   | `#67c23a` |
| fixedRow      | 固定行判定函数（配合 `FixedRowPlugin`）                | `(row, rowIndex) => ('top'\|'bottom'\|boolean)` | 否   | -         |
| fixedRowClass | 预留配置项（当前逻辑以 `FixedRowPlugin` 输出类名为准） | `string`                                        | 否   | -         |

## 列配置 ColumnConfig（常用字段）

| 字段                              | 说明                                                                                    |
| --------------------------------- | --------------------------------------------------------------------------------------- |
| prop                              | 列字段名（叶子列必填）                                                                  |
| label                             | 表头标题（必填）                                                                        |
| width / fixed / align / formatter | 与 Element-UI `el-table-column` 一致                                                    |
| children                          | 多级表头                                                                                |
| merge                             | `true` 时按同值连续行计算纵向合并                                                       |
| formula                           | 公式表达式（示例：`a + b`、`price * qty`）                                              |
| digits                            | 数值精度（小数位数）                                                                    |
| editable                          | 是否允许触发可编辑占位点击                                                              |
| type / direction / anchor         | 虚拟列配置：`type: 'virtual'`；`direction: 'vertical'` 时必须提供 `anchor` 与 `formula` |

## 事件

| 事件名             | 说明                     | 回调参数                                                     |
| ------------------ | ------------------------ | ------------------------------------------------------------ |
| cell-click         | 单元格点击（通用事件）   | `{ row, column, rowIndex, columnIndex, value }`              |
| cell-edit          | 数值列编辑（可更新数据） | `{ row, column, rowIndex, columnIndex, currentValue, done }` |
| formula-mouseenter | 公式图标 hover           | `ColumnConfig`                                               |
| formula-mouseleave | 公式图标 leave           | `ColumnConfig`                                               |

## 实例方法（通过 ref 调用）

| 方法                                     | 说明                                                          |
| ---------------------------------------- | ------------------------------------------------------------- |
| getExportData()                          | 获取可导出的 `{ data, columns, timestamp }`（会剔除内部字段） |
| processData()                            | 手动触发一次公式/合并/分组汇总计算                            |
| toggleColumnEditable({ prop, editable }) | 切换某列可编辑状态                                            |
| toggleAllColumnsEditable(editable)       | 切换全部列可编辑状态                                          |

## 工具函数 utils（对外导出）

从包中直接导入：`import { utils } from 'el-excel-table'`

| 方法                                                        | 说明                                      |
| ----------------------------------------------------------- | ----------------------------------------- |
| utils.processTableData                                      | 处理数据（公式 → 合并 → 分组汇总）        |
| utils.createExcelExporter                                   | 创建 Excel 导出器（注入 XLSX 后直接导出） |
| utils.createPdfExporter                                     | 创建 PDF 导出器（注入 jsPDF 后直接导出）  |
| utils.findColumn / utils.find_column                        | 查找列配置                                |
| utils.extractMergeKeys / utils.extract_merge_keys           | 提取 merge 列                             |
| utils.extractEditableStates / utils.extract_editable_states | 提取 editable 状态                        |
| utils.expandSummaryColumns / utils.expand_summary_columns   | 标准化列配置（横向虚拟列自动生成 prop）   |

## 样式说明（可选）

组件内部会添加以下 class，你可以按需补充样式：

```css
/* 合并单元格的提示样式（可选） */
.cell-merged {
  background: #fafafa;
}

/* 公式依赖高亮（FormulaHighlighter 插件会用到） */
th.formula-highlight {
  background: #fff7e6;
}

/* 固定行（FixedRowPlugin 会输出这些类名） */
.fixed-row-top td,
.fixed-row-bottom td {
  position: sticky;
  z-index: 2;
  background: #fff;
}
```

## 导出 PDF 注意事项

- 业务侧自行引入 `jsPDF` 与 `autoTable`
- 推荐使用 `utils.createPdfExporter({ jsPDF })`，内部会 `new jsPDF(...)` + `doc.autoTable(...)` + `doc.save(...)`
