Vue + excel下载 + 水印

需要引入两个js库

import ExcelJS from 'exceljs'

import { saveAs } from 'file-saver'

复制代码
<template>
  <div class="excel-watermark-component">
    <button 
      @click="exportExcelWithWatermark" 
      class="download-btn"
      :disabled="isExporting"
    >
      <i class="fas fa-file-excel"></i>
      {{ isExporting ? '正在生成...' : '下载带水印Excel' }}
    </button>
  </div>
</template>

<script>
import ExcelJS from 'exceljs'
import { saveAs } from 'file-saver'

export default {
  name: 'ExcelWatermark',
  data() {
    return {
      isExporting: false,
      watermarkText: 'CONFIDENTIAL',
      filename: '带水印Excel文件',
      sampleData: [
        {
          '姓名': '张三',
          '部门': '技术部',
          '工资': '15000',
          '奖金': '5000'
        },
        {
          '姓名': '李四',
          '部门': '市场部',
          '工资': '12000',
          '奖金': '3000'
        },
        {
          '姓名': '王五',
          '部门': '财务部',
          '工资': '13000',
          '奖金': '4000'
        }
      ]
    }
  },
  methods: {
    // 生成重复平铺的水印图片
    generateTiledWatermarkImage(text) {
      const tileSize = 400 // 单个水印图块尺寸
      const canvas = document.createElement('canvas')
      canvas.width = tileSize
      canvas.height = tileSize
      const ctx = canvas.getContext('2d')
      
      // 设置透明背景
      ctx.clearRect(0, 0, canvas.width, canvas.height)

      // 保存当前状态
      ctx.save()
      // 移动到中心点并旋转
      ctx.translate(tileSize / 2, tileSize / 2)
      ctx.rotate(-30 * Math.PI / 180) // 30度转换为弧度
      
      // 设置文字样式
      ctx.font = 'bold 30px Arial'
      ctx.fillStyle = 'rgba(128, 128, 128, 0.1)' // 浅灰色半透明
      ctx.textAlign = 'center'
      ctx.textBaseline = 'middle'
      
      // 在中心绘制水印文字
      ctx.fillText(text, 0, 0)

      ctx.restore()
      
      return canvas.toDataURL('image/png').split(',')[1] // 返回base64数据部分
    },

    // 导出带水印的Excel文件
    async exportExcelWithWatermark() {
      if (this.isExporting) return
      
      try {
        this.isExporting = true
        
        // 创建工作簿
        const workbook = new ExcelJS.Workbook()
        workbook.created = new Date()
        workbook.modified = new Date()
        
        // 创建工作表
        const worksheet = workbook.addWorksheet('数据表')
        
        // 添加示例数据
        const headers = Object.keys(this.sampleData[0])
        const headerRow = worksheet.addRow(headers)
        
        // 设置表头样式
        headerRow.eachCell((cell, colNumber) => {
          cell.fill = {
            type: 'pattern',
            pattern: 'solid',
            fgColor: { argb: 'FFE8E8E8' }
          }
          cell.font = {
            bold: true,
            size: 12
          }
          cell.border = {
            top: { style: 'thin' },
            left: { style: 'thin' },
            bottom: { style: 'thin' },
            right: { style: 'thin' }
          }
        })
        
        // 添加数据行
        this.sampleData.forEach(item => {
          const rowData = headers.map(header => item[header])
          console.log(rowData);
          worksheet.addRow(rowData)
        })
        
        // 生成水印图片并设置为背景
        const base64Watermark = this.generateTiledWatermarkImage(this.watermarkText)
        const imageId = workbook.addImage({
          base64: base64Watermark,
          extension: 'png'
        })
        
        // 设置水印为重复平铺背景
        worksheet.addBackgroundImage(imageId)
        
        // 设置列宽
        worksheet.columns = headers.map(() => ({ width: 15 }))
        
        // 生成Excel文件并下载
        const buffer = await workbook.xlsx.writeBuffer();
        const blob = new Blob([buffer], {
          type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
        });
        saveAs(blob, `${this.filename}.xlsx`)
        
        this.$message.success('Excel文件导出成功!')
      } catch (error) {
        console.error('导出Excel文件时出错:', error)
        this.$message.error('导出失败:' + error.message)
      } finally {
        this.isExporting = false
      }
    }
  }
}
</script>

<style scoped>
.excel-watermark-component {
  display: inline-block;
}

.download-btn {
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  color: white;
  padding: 12px 24px;
  border: none;
  border-radius: 8px;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  transition: all 0.3s ease;
  border: 2px solid transparent;
}

.download-btn:hover:not(:disabled) {
  transform: translateY(-2px);
  box-shadow: 0 8px 25px rgba(102, 126, 234, 0.4);
}

.download-btn:disabled {
  background: #a0aec0;
  cursor: not-allowed;
}

.download-btn i {
  margin-right: 8px;
}
</style>

如果是vue3 直接把methods中的方法复制一下就行

相关推荐
Emily156853598701 小时前
Emerson 1C31129G03 Analog Input Module
java·开发语言·前端·plc·emerson·1c31129g03·input module
To_OC6 小时前
面试被问了三回三栏布局,这次我终于把 BFC 那层窗户纸捅破了
前端·css·面试
To_OC7 小时前
啃完 TS 工具类型我发现:Pick 和 Omit 原来就是一层窗户纸
前端·面试·typescript
风月说与山鬼8 小时前
三、大括号语法
前端·react.js
kyriewen8 小时前
我把最常踩的8个CORS跨域报错整理了一遍——第8个去年还不存在
前端·javascript
Csvn9 小时前
🕰️ 闭包 + setTimeout 的 5 个经典陷阱:为什么定时器看到的永远不是最新的值?
前端
lilian2339 小时前
Harmony os 技术实战|拼豆制图27:用单字符编码承载 50 张 70×70 图纸
前端·数据库·华为·harmonyos
CarIise10 小时前
CSS选择器与样式关联
前端·css·tensorflow
里欧跑得慢11 小时前
CSS 模块化架构的演进:BEM、CSS Modules 到 CSS-in-JS 的反思
前端·css·flutter·web·css-in-js