前端excel的实现方案Luckysheet

发布于:2024-12-21 ⋅ 阅读:(31) ⋅ 点赞:(0)

一、介绍

Luckysheet是一款纯前端类似excel的在线表格,功能强大、配置简单、完全开源的插件。目前已暂停维护,但是其已有功能大概能满足常见需求的使用。

在这里插入图片描述

二、引入

①cdn引入(目前应该已经不支持,可自行尝试)

<link rel='stylesheet' href='https://cdn.jsdelivr.net/npm/luckysheet@latest/dist/plugins/css/pluginsCss.css' />
<link rel='stylesheet' href='https://cdn.jsdelivr.net/npm/luckysheet@latest/dist/plugins/plugins.css' />
<link rel='stylesheet' href='https://cdn.jsdelivr.net/npm/luckysheet@latest/dist/css/luckysheet.css' />
<link rel='stylesheet' href='https://cdn.jsdelivr.net/npm/luckysheet@latest/dist/assets/iconfont/iconfont.css' />
<script src="https://cdn.jsdelivr.net/npm/luckysheet@latest/dist/plugins/js/plugin.js"></script>
<script src="https://cdn.jsdelivr.net/npm/luckysheet@latest/dist/luckysheet.umd.js"></script>

②本地引入
需使用npm或者从其他资源处获取luckysheet包,将dist中全部文件放在项目文件夹中,随后在html页面进行相关文件的引用。

  <link rel='stylesheet' href='./plugins/css/pluginsCss.css' />
  <link rel='stylesheet' href='./plugins/plugins.css' />
  <link rel='stylesheet' href='./css/luckysheet.css' />
  <link rel='stylesheet' href='./assets/iconfont/iconfont.css' />
  <script src="./plugins/js/plugin.js"></script>
  <script src="./luckysheet.umd.js"></script>

三、使用

1、初始化
// 插件配置项
var options = {
    container: 'luckysheet', //luckysheet为容器id
    title: 'Luckysheet Demo', // 设定表格名称
    lang: 'zh' // 设定表格语言
}
window.luckysheet.create(options)

针对个性化的需求,除了允许配置信息栏(showinfobar)、工具栏(showtoolbar)、底部sheet页(showsheetbar)、底部计数栏(showstatisticBar)之外, Luckysheet开放了更细致的自定义配置选项,分别有:

①自定义工具栏(showtoolbarConfig)
②自定义底部sheet页(showsheetbarConfig)
③自定义计数栏(showstatisticBarConfig)
④自定义单元格右键菜单(cellRightClickConfig)
⑤自定义底部sheet页右击菜单(sheetRightClickConfig)

2、表格配置项

表格初始化配置options时,需要配置一个由每个工作表参数组成的一维数组,赋给options.data。

options.data示例如下:

[
    {
        "name": "Cell", //工作表名称
        "color": "", //工作表颜色
        "index": 0, //工作表索引
        "status": 1, //激活状态
        "order": 0, //工作表的下标
        "hide": 0,//是否隐藏
        "row": 36, //行数
        "column": 18, //列数
        "defaultRowHeight": 19, //自定义行高
        "defaultColWidth": 73, //自定义列宽
        "celldata": [], //初始化使用的单元格数据
        "config": {
            "merge":{}, //合并单元格
            "rowlen":{}, //表格行高
            "columnlen":{}, //表格列宽
            "rowhidden":{}, //隐藏行
            "colhidden":{}, //隐藏列
            "borderInfo":{}, //边框
            "authority":{}, //工作表保护
            
        },
        "scrollLeft": 0, //左右滚动条位置
        "scrollTop": 315, //上下滚动条位置
        "luckysheet_select_save": [], //选中的区域
        "calcChain": [],//公式链
        "isPivotTable":false,//是否数据透视表
        "pivotTable":{},//数据透视表设置
        "filter_select": {},//筛选范围
        "filter": null,//筛选配置
        "luckysheet_alternateformat_save": [], //交替颜色
        "luckysheet_alternateformat_save_modelCustom": [], //自定义交替颜色	
        "luckysheet_conditionformat_save": {},//条件格式
        "frozen": {}, //冻结行列配置
        "chart": [], //图表配置
        "zoomRatio":1, // 缩放比例
        "image":[], //图片
        "showGridLines": 1, //是否显示网格线
        "dataVerification":{} //数据验证配置
    },
    {
        "name": "Sheet2",
        "color": "",
        "index": 1,
        "status": 0,
        "order": 1,
        "celldata": [],
        "config": {}
    },
    {
        "name": "Sheet3",
        "color": "",
        "index": 2,
        "status": 0,
        "order": 2,
        "celldata": [],
        "config": {},
    }
]
3、数据的输入和输出

①初始化输入
表格中的数据初始化与表对象中的celldata相关。celldata对象是原始单元格数据集,存储sheet中所有单元格中的值,是一个包含{r:0,c:0,v:{m:“value”,v:“value”,ct: {fa: “General”, t: “g”}}}格式单元格信息的一维数组,只在初始化的时候使用。

r代表行,c代表列,v代表该单元格的值,值可以是字符、数字或者对象。

Luckysheet在建立的时候会根据 options.data[i].row 和 options.data[i].column 的行列数量大小新建一个表格data,然后再使用 data[r][c]=v 的方式填充表格数据,空数据单元格以null表示。

使用celldata初始化完表格后,数据转换为luckysheetfile中的字段data,如luckysheetfile[i].data,后续操作表格的数据更新,会更新到这个data字段中,celldata不再使用。

[{
    "r": 0,
    "c": 0,
    "v": {
        ct: {fa: "General", t: "g"},
        m:"value1", // m为表格显示的值
        v:"value1" // 不显示但可以获取的实际值
    },
    value: 100
}, {
    "r": 0,
    "c": 1,
    "v": {
        ct: {fa: "General", t: "g"},
        m:"value2",
        v:"value2"
    },
    value: 100
}]

②输出

可使用插件自带的API获取表格最新的数据。这里需要注意的问题是,导出的数据中,通常m和v的值是一致的,疑是插件的bug。所以当我我们定义了单元格中不同的m和v值,同时想要调用以下方法获得单元格的实际v值时,可自定义一个value值的方式得到我们想要的单元格实际值。

window.luckysheet.getAllSheets()[0].data

③导出

需安装 exceljs 插件,luckysheet提供数据处理,文件的写入由exceljs负责。

exportExcel(window.luckysheet.getluckysheetfile()).then((data) => {
        const blob = new Blob([data], {
          type: "application/vnd.ms-excel;charset=utf-8",
        });
        this.$download.saveAs(blob, "xxx");
      });


const Excel = require('exceljs')

export async function exportExcel (luckysheet) { // 参数为luckysheet.getluckysheetfile()获取的对象
	// 1.创建工作簿,可以为工作簿添加属性
	const workbook = new Excel.Workbook()
	// 2.创建表格,第二个参数可以配置创建什么样的工作表
	luckysheet.every(function (table) {
		if (table.data.length === 0) return true
		const worksheet = workbook.addWorksheet(table.name)
		// 3.设置单元格合并,设置单元格边框,设置单元格样式,设置值
		setStyleAndValue(table.data, worksheet)
		// setMerge(table.config.merge, worksheet)
		// setBorder(table.config.borderInfo, worksheet)
		return true
	})
	// 4.写入 buffer
	const buffer = await workbook.xlsx.writeBuffer()
	return buffer
}

var setMerge = function (luckyMerge = {}, worksheet) {
	const mergearr = Object.values(luckyMerge)
	mergearr.forEach(function (elem) { // elem格式:{r: 0, c: 0, rs: 1, cs: 2}
		// 按开始行,开始列,结束行,结束列合并(相当于 K10:M12)
		worksheet.mergeCells(elem.r + 1, elem.c + 1, elem.r + elem.rs, elem.c + elem.cs)
	})
}

var setBorder = function (luckyBorderInfo, worksheet) {
	if (!Array.isArray(luckyBorderInfo)) return
	luckyBorderInfo.forEach(function (elem) {
		let border = borderConvert(elem.borderType, elem.style, elem.color)
		let rang = elem.range[0]
		// console.log(rang.column_focus + 1, rang.row_focus + 1)
		worksheet.getCell(rang.row_focus + 1, rang.column_focus + 1).border = border
	})
}
var setStyleAndValue = function (cellArr, worksheet) {
	if (!Array.isArray(cellArr)) return
	cellArr.forEach(function (row, rowid) {
		row.every(function (cell, columnid) {
			if (!cell) return true
			let fill = fillConvert(cell.bg)
			let font = fontConvert(cell.ff, cell.fc, cell.bl, cell.it, cell.fs, cell.cl, cell.ul)
			let alignment = alignmentConvert(cell.vt, cell.ht, cell.tb, cell.tr)
			let value
			if (cell.f) {
				value = { formula: cell.f, result: cell.v }
			} else {
				value = cell.v
			}
			let target = worksheet.getCell(rowid + 1, columnid + 1)
			target.fill = fill
			target.font = font
			target.alignment = alignment
			target.value = value
			return true
		}) 
	})
}

var fillConvert = function (bg) {
	if (!bg) {
		return {}
	}
	let fill = {
		type: 'pattern',
		pattern: 'solid',
		fgColor: {argb: bg.replace('#', '')}
	}
	return fill
}

var fontConvert = function (ff = 0, fc = '#000000', bl = 0, it = 0, fs = 10, cl = 0, ul = 0) { // luckysheet:ff(样式), fc(颜色), bl(粗体), it(斜体), fs(大小), cl(删除线), ul(下划线)
	const luckyToExcel = {
		0: '微软雅黑',
		1: '宋体(Song)',
		2: '黑体(ST Heiti)',
		3: '楷体(ST Kaiti)', 
		4: '仿宋(ST FangSong)', 
		5: '新宋体(ST Song)', 
		6: '华文新魏', 
		7: '华文行楷', 
		8: '华文隶书', 
		9: 'Arial', 
		10: 'Times New Roman ',
		11: 'Tahoma ',
		12: 'Verdana',
		num2bl: function (num) {
			return num === 0 ? false : true
		}
	}
	
	let font = {
		name: luckyToExcel[ff],
		family: 1,
		size: fs,
		color: {argb: fc.replace('#', '')},
		bold: luckyToExcel.num2bl(bl),
		italic: luckyToExcel.num2bl(it),
		underline: luckyToExcel.num2bl(ul),
		strike: luckyToExcel.num2bl(cl)
	}
	
	return font 
}

var alignmentConvert = function (vt = 'default', ht = 'default', tb = 'default', tr = 'default') { // luckysheet:vt(垂直), ht(水平), tb(换行), tr(旋转)
	const luckyToExcel = {
		vertical: {
			0: 'middle',
			1: 'top',
			2: 'bottom',
			default: 'top'
		},
		horizontal: {
			0: 'center',
			1: 'left',
			2: 'right',
			default: 'left'
		},
		wrapText: {
			0: false,
			1: false,
			2: true,
			default: false
		},
		textRotation: {
			0: 0,
			1: 45,
			2: -45,
			3: 'vertical',
			4: 90,
			5: -90,
			default: 0
		}
	}
	
	let alignment = {
		vertical: luckyToExcel.vertical[vt],
		horizontal: luckyToExcel.horizontal[ht],
		wrapText: luckyToExcel.wrapText[tb],
		textRotation: luckyToExcel.textRotation[tr]
	}
	return alignment
	
}

var borderConvert = function (borderType, style = 1, color = '#000') { // 对应luckysheet的config中borderinfo的的参数
	if (!borderType) {
		return {}
	}
	const luckyToExcel = {
		type: {
			'border-all': 'all',
			'border-top': 'top',
			'border-right': 'right',
			'border-bottom': 'bottom',
			'border-left': 'left'
		},
		style: {
			0: 'none',
			1: 'thin',
			2: 'hair',
			3: 'dotted',
			4: 'dashDot', // 'Dashed',
			5: 'dashDot',
			6: 'dashDotDot',
			7: 'double',
			8: 'medium',
			9: 'mediumDashed',
			10: 'mediumDashDot',
			11: 'mediumDashDotDot',
			12: 'slantDashDot',
			13: 'thick'
		}
	}
	let template = {style: luckyToExcel.style[style], color: {argb: color.replace('#', '')}}
	let border = {}
	if (luckyToExcel.type[borderType] === 'all') {
		border['top'] = template
		border['right'] = template
		border['bottom'] = template
		border['left'] = template
	} else {
		border[luckyToExcel.type[borderType]] = template
	}
	return border
}