前端excel的实现方案Luckysheet

一、介绍

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
}
相关推荐
战场小包几秒前
小米su7 or 保时捷怎么选?使用 Three 实现 3D 汽车展示平台比比看
前端·vue.js·3d·aigc
漫天绯羽31 分钟前
使用echarts 绘制县级以下 乡镇地图并标注若干坐标点。获取县级以下乡镇的边界坐标
前端·javascript·echarts
停留的章小鱼33 分钟前
vue3项目结合Echarts实现甘特图(可拖拽、选中等操作)
前端·vue.js·echarts·甘特图
门时1 小时前
Chrome 浏览器原生功能截长屏
前端·chrome
乐闻x1 小时前
Web 应用如何使用sqlite?使用 sql.js 实现前端 SQLite 数据库操作
前端·数据库·sqlite
1024小神1 小时前
tauri2中创建新的窗口方式,和tauri1不一样了哦
java·服务器·前端
小彭努力中1 小时前
38.在 Vue 3 中使用 OpenLayers 导出地图为 PDF
前端·javascript·vue.js·深度学习·pdf·openlayers
黄公子学安全1 小时前
网安入门|前端基础之Html_css基础
前端·css·html
布兰妮甜2 小时前
图片和媒体资源的优化:提升Web应用性能与用户体验的关键
前端·媒体·优化·图片
lvbb663 小时前
React的img图片路径怎么写
前端·javascript·react.js