跳至主要內容

Exceljs

俞文健大约 3 分钟

单元格样式

单元格可以设置的样式。

interface Style {
  // 字体
	font: Partial<Font>
  	
  // 对齐
  alignment: Partial<Alignment>
  
  // 边框
  border: Partial<Borders>
  
  // 填充模式
	fill: Fill
  
  // 数字格式
	numFmt: string
	
  // 单元格保护
	protection: Partial<Protection>
}

字体样式

interface Font {
  // 字体族 'Arial', 'Calibri' ...
	name: string
  
  // 备用字体族 1 - Serif, 2 - Sans Serif, 3 - Mono, Others - unknown
	family: number
  
  // 字体大小
	size: number
  
  // 字体颜色
	color: { argb: string; theme: number }
  
  // 字体加粗
	bold: boolean
  
  // 字体倾斜
	italic: boolean
  
  // 下划线
	underline: boolean | 'none' | 'single' | 'double' | 'singleAccounting' | 'doubleAccounting'
  
  // 删除线
	strike: boolean
  
  // 字体轮廓
	outline: boolean
  
  // 垂直对齐
	vertAlign: 'superscript' | 'subscript'
  
  // 字体方案
	scheme: 'minor' | 'major' | 'none'
  
  // 字符集
	charset: number
}

文本对齐

interface Alignment {
  // 水平对齐
	horizontal: 'left' | 'center' | 'right' | 'fill' | 'justify' | 'centerContinuous' | 'distributed'
  
  // 垂直对齐
	vertical: 'top' | 'middle' | 'bottom' | 'distributed' | 'justify'
  
  // 文本换行
	wrapText: boolean
  
  // 自适应
	shrinkToFit: boolean
  
  // 缩进
	indent: number
  
  // 阅读顺序
	readingOrder: 'rtl' | 'ltr'
  
  // 文本旋转
	textRotation: number | 'vertical'
}

边框样式

interface Borders {
  // 对角线
	diagonal: Partial<BorderDiagonal>
  
	top: Partial<Border>
	left: Partial<Border>
	bottom: Partial<Border>
	right: Partial<Border>
}

interface Border {
  // 边框样式
	style: 
    | 'thin' | 'dotted' | 'hair' | 'medium' | 'double' | 'thick' | 'dashDot'
	  | 'dashDotDot' | 'slantDashDot' | 'mediumDashed' | 'mediumDashDotDot' | 'mediumDashDot'
  
  // 边框颜色
	color: { argb: string; theme: number }
}

interface BorderDiagonal extends Border {
	up: boolean
	down: boolean
}

填充

type Fill = FillPattern | FillGradientAngle | FillGradientPath

interface FillPattern {
  // 普通模式
	type: 'pattern'
  
  // 模式类型
	pattern: 
    | 'none' | 'solid'
	  | 'darkVertical' | 'darkHorizontal' | 'darkGrid' | 'darkTrellis' | 'darkDown' | 'darkUp'
	  | 'lightVertical' | 'lightHorizontal' | 'lightGrid' | 'lightTrellis' | 'lightDown'
    | 'lightUp'
	  | 'darkGray' | 'mediumGray' | 'lightGray' | 'gray125' | 'gray0625'
  
  // 图案前景色
	fgColor?: { argb: string; theme: number }
  
  // 图案背景色
	bgColor?: { argb: string; theme: number }
}

interface FillGradientAngle {
  // 渐变模式
	type: 'gradient'
  
  // 线性渐变
	gradient: 'angle'
  
  // 旋转角度
	degree: number
  
  // 渐变颜色序列
	stops: { position: number; color: { argb: string; theme: number } }[]
}

interface FillGradientPath {
  // 渐变模式
	type: 'gradient'
  
  // 径向渐变
	gradient: 'path'
  
  // 中心点
	center: { left: number; top: number }
  
  // 渐变颜色序列
	stops: { position: number; color: { argb: string; theme: number } }[]
}

导出函数

useExport.ts

导出表格,第一列为序号,第二列为图片。

import { ref } from "vue"
import Exceljs from "exceljs"
import FileSaver from "file-saver"

// 进度
const progress = ref(0)

const worker = new Worker("worker.js")

const listener = ref<((event: MessageEvent) => void) | null>(null)

export default function useExport<T, K>() {
  const toExcel = async ({ data, headers }: {
    data: T
    headers: K
  }) => {
    progress.value = data.length
    
    const workbook = new Exceljs.Workbook()
    const sheet = workbook.addWorksheet("sheet")
    sheet.columns = headers
    sheet.addRows(data)
    for (let i = 1; i <= headers.length; i++) { // 列
      for (let j = 1; j <= data.length + 1; j++) { // 行
        // 设置单元格样式
      }
      // 设置行样式
    }
    // [empty, "图片", url, url, url ...]
    const urls = sheet.getColumn(2).values.slice(2)
    
    worker.postMessage({ workbook, sheet, urls })
    
    listener.value = async (event: MessageEvent) => {
      const { row, base64 } = event.data
      
      progress.value--
      
      const imageId = workbook.addImage({
        base64: base64.toString(),
        extension: "jpeg"
      })
      // 清空 url 文本,只显示图片
      sheet.getCell(`B${row + 1}`).value = ""
      sheet.addImage(imageId, {
        tl: { row, col: 1 },
        ext: { width: 120, height: 120 }
      })
      if (row === urls.length) {
        await workbook.xlsx.writeBuffer().then(buffer => {
          const _file = new Blob([buffer], { type: "application/octet-stream" })
          FileSaver.saveAs(_file, "Excel.xlsx")
        })
        
        worker.removeEventListener("message", listener.value!)
      }
    }
    
    worker.addEventListener("message", listener.value)
  }
  
  return { progress, toExcel }
}