前端实现Excel表格数据导入和提交功能(三)

原创
10/10 15:30
阅读数 243

界面优化(以Layui框架为例)

准备工作

下载Layui框架。

下载地址:https://gitee.com/sentsin/layui

Layui项目下载

将下载好的文件解压,并将其中的src目录下的所有文件复制到项目目录。

Layui项目目录1

Layui项目目录2

引入框架

将layui框架相关文件都复制进项目文件后,就可以开始引入框架到页面中进行使用了。

第一步,在<head>标签中添加样式引用,href属性的值请变更为自己项目中layui.css文件的路径。

<link rel="stylesheet" type="text/css" href="../../layui/css/layui.css"/>

第二步,将原先引入jQuery的脚本标签的src属性变更为自己项目中layui.js文件的路径,layui框架自带jQuery模块,因此无需再引入jQuery相关文件。

<script src="../../layui/layui.js" type="text/javascript" charset="utf-8"></script>

第三步,修改之前的脚本标签内的js代码。

/**
 * layui框架入口方法。低版本的layui框架,还需要在该匿名函数前添加一个参数,值为一个包含了所需要使用的layui模块名称的数组,例:['table','layer']
 * @param {Object} args
 */
layui.use(function(args) {
    // 引用layui框架的数据表格、弹出层、jQuery、元素模块
    var table = layui.table,
        layer = layui.layer,
        $ = layui.$,
        element = layui.element;
    
    /**
     * 将之前的js代码复制至此处
     */
});

完善页面(表格展示解析结果,进度条显示提交进度)

在页面的<head>标签中添加内联样式。

<style type="text/css">
    .tool-head {
        box-sizing: border-box;
        width: 100%;
        height: 30px;
        margin: 10px 0;
        padding: 0 20px;
    }
			
    .hidden {
        display: none;
    }
			
    .show {
        display: block;
    }
</style>

修改<body>标签中的内容。

由于文件选择控件的样式调整起来较为麻烦,我这里采用将其隐藏,而用更加方便美化的按钮显示在页面上的方案。

<div class="tool-head">
    <!-- 文件控件 -->
    <input type="file" accept=".xlsx, .xls" id="uploadFile" style="display: none;"/>
    <!-- 文件按钮 -->
    <btn id="uploadFileBtn" class="layui-btn">选择文件</btn>
    <!-- 提交按钮 -->
    <btn class="layui-btn layui-btn-normal" id="submitBtn">上传</btn>
    <!-- 进度条 -->
    <div id="progressBox" class="hidden" style="float: right; width: 400px; margin-top: 15px;">
        <div style="clear: both;" id="progressShow" class="layui-progress layui-progress-big"  lay-filter="submitShow" lay-showPercent="true">
            <div class="layui-progress-bar layui-bg-red" lay-percent="0%"></div>
        </div>
    </div>
</div>
<!-- 表格 -->
<table id="showDataTable"></table>

在脚本标签中的匿名函数(layui.use()方法的参数)内的变量声明语句(引入layui模块的那条)后添加js代码。

注:原先js代码中,遍历解析时添加的ajax方法需删去。

// 定义数据集,存储Excel解析结果
let datas = new Array();
// 定义已完成数
let finishNum = 0;
// 定义总数
let sum = datas.length;
// 定义错误数
let errNum = 0;
				
/**
 * 变更进度条
 */
function changeProgress() {
    $('.layui-progress-bar').attr('lay-percent', finishNum + '/' + sum);
    element.init();
    element.progress('submitShow', finishNum + '/' + sum);
}
				
// 渲染数据表格,用于呈现Excel解析结果
table.render({
    id: 'dataTable',
    elem: '#showDataTable', // 对应<table>标签的id属性值
    height: 860,
    data: datas, // 指定静态数据 
    page: true, // 开启分页
    cols: [[ // 定义表头
        {field: 'id', title: '序号', width: '15%', sort: true, fixed: 'left'},
        {field: 'name', title: '姓名', width: '25%', sort: true},
        {field: 'gender', title: '性别', width: '15%', sort: true},
        {field: 'age', title: '年龄', width: '20%', sort: true},
        {field: 'phone', title: '联系方式', width: '25%', sort: true}
    ]]
});
				
// 使用户点击文件按钮的效果 等同于 点击文件控件
$('#uploadFileBtn').click(function() {
    $('#uploadFile').click();
});
				
// 遍历解析结果并分条提交
$('#submitBtn').click(function() {
    if (datas.length != 0) {
        sum = datas.length;
        // 获取按钮元素
        let uploadFileBtn = $('#uploadFileBtn');
        let submitBtn = $('#submitBtn');
        // 禁用按钮
        uploadFileBtn.attr('disabled', 'disabled');
        submitBtn.attr('disabled', 'disabled');
        // 显示进度条框
        $('#progressBox').removeClass('hidden').addClass('show');
        // 初始化进度条进度
        changeProgress();
        for (let item of datas) {
            // 提交数据
            $.ajax({
                url: 'http://localhost:22911/showproject/test/uploadExcelData', // 后端提供的请求地址
                type: 'post',
                data: JSON.stringify(item),
                dataType: 'json',
                contentType: 'application/json;charset=utf-8',
                success: function(res) {
                    finishNum++;
                    // 更新进度
                    changeProgress();
                    if (finishNum == sum) {
                        layer.msg('提交完成!');
                        // 启用按钮
                        uploadFileBtn.removeAttr('disabled');
                        submitBtn.removeAttr('disabled');
                    }
                },
                error: function() {
                    finishNum++;
                    changeProgress();
                    errNum++;
                    if (finishNum == sum) {
                        layer.msg('提交完成!失败次数:' + errNum);
                        // 启用按钮
                        uploadFileBtn.removeAttr('disabled');
                        submitBtn.removeAttr('disabled');
                    }
                }
            });
        }
    } else {
        layer.msg("未选择有效的Excel文件,无法上传!");
    }
});

效果展示

上传演示1

上传演示2

上传演示3

完整代码

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8" />
		<title>前端解析并提交Excel表格数据演示</title>
		<link rel="stylesheet" type="text/css" href="../../layui/css/layui.css"/>
		<style type="text/css">
			.tool-head {
				box-sizing: border-box;
				width: 100%;
				height: 30px;
				margin: 10px 0;
				padding: 0 20px;
			}
			
			.hidden {
				display: none;
			}
			
			.show {
				display: block;
			}
		</style>
	</head>
	<body>
		<div class="tool-head">
			<!-- 文件控件,必要的标签 -->
			<input type="file" accept=".xlsx, .xls" id="uploadFile" style="display: none;"/>
			<!-- 文件按钮,目的是替代文件控件的显示以便样式美化,非必须 -->
			<btn id="uploadFileBtn" class="layui-btn">选择文件</btn>
			<!-- 提交按钮 -->
			<btn class="layui-btn layui-btn-normal" id="submitBtn">上传</btn>
			<!-- 进度条 -->
			<div id="progressBox" class="hidden" style="float: right; width: 400px; margin-top: 15px;">
				<div style="clear: both;" id="progressShow" class="layui-progress layui-progress-big"  lay-filter="submitShow" lay-showPercent="true">
					<div class="layui-progress-bar layui-bg-red" lay-percent="0%"></div>
				</div>
			</div>
		</div>
		<!-- 表格 -->
		<table id="showDataTable"></table>
		<!-- 引入Excel处理工具库xlsx.js,解析Excel就靠它了 -->
		<script src="../../js/xlsx.core.min.js" type="text/javascript" charset="utf-8"></script>
		<!-- 引入layui框架所需js文件 -->
		<script src="../../layui/layui.js" type="text/javascript" charset="utf-8"></script>
		<!-- 脚本,实现选择Excel文件后解析数据并封装成对象 -->
		<script type="text/javascript">
			/**
			 * layui框架入口方法。低版本的layui框架,还需要在该匿名函数前添加一个参数,值为一个包含了所需要使用的layui模块名称的数组,例:['table','layer']
			 * @param {Object} args
			 */
			layui.use(function(args) {
				// 引用layui框架的数据表格、弹出层、jQuery、元素模块
				var table = layui.table,
				layer = layui.layer,
				$ = layui.$,
				element = layui.element;
				
				// 定义数据集,存储Excel解析结果
				let datas = new Array();
				// 定义已完成数
				let finishNum = 0;
				// 定义总数
				let sum = datas.length;
				// 定义错误数
				let errNum = 0;
				
				/**
				 * 变更进度条
				 */
				function changeProgress() {
					$('.layui-progress-bar').attr('lay-percent', finishNum + '/' + sum);
					element.init();
					element.progress('submitShow', finishNum + '/' + sum);
				}
				
				// 渲染数据表格,用于呈现Excel解析结果
				table.render({
					id: 'dataTable',
					elem: '#showDataTable', // 对应<table>标签的id属性值
					height: 860,
					data: datas, // 指定静态数据 
					page: true, // 开启分页
					cols: [[ // 定义表头
						{field: 'id', title: '序号', width: '15%', sort: true, fixed: 'left'},
						{field: 'name', title: '姓名', width: '25%', sort: true},
						{field: 'gender', title: '性别', width: '15%', sort: true},
						{field: 'age', title: '年龄', width: '20%', sort: true},
						{field: 'phone', title: '联系方式', width: '25%', sort: true}
					]]
				});
				
				// 使用户点击文件按钮的效果 等同于 点击文件控件
				$('#uploadFileBtn').click(function() {
					$('#uploadFile').click();
				});
				
				// 遍历解析结果并分条提交
				$('#submitBtn').click(function() {
					if (datas.length != 0) {
						totalNum = datas.length;
						// 获取按钮元素
						let uploadFileBtn = $('#uploadFileBtn');
						let submitBtn = $('#submitBtn');
						// 禁用按钮
						uploadFileBtn.attr('disabled', 'disabled');
						submitBtn.attr('disabled', 'disabled');
						// 显示进度条框
						$('#progressBox').removeClass('hidden').addClass('show');
						// 初始化进度条进度
						changeProgress();
						for (let item of datas) {
							// 提交数据
							$.ajax({
								url: 'http://localhost:22911/showproject/test/uploadExcelData', // 后端提供的请求地址
								type: 'post',
								data: JSON.stringify(item),
								dataType: 'json',
								contentType: 'application/json;charset=utf-8',
								success: function(res) {
									finishNum++;
									// 更新进度
									changeProgress();
									if (finishNum == sum) {
										layer.msg('提交完成!');
										// 启用按钮
										uploadFileBtn.removeAttr('disabled');
										submitBtn.removeAttr('disabled');
									}
								},
								error: function() {
									finishNum++;
									changeProgress();
									errNum++;
									if (finishNum == sum) {
										layer.msg('提交完成!失败次数:' + errNum);
										// 启用按钮
										uploadFileBtn.removeAttr('disabled');
										submitBtn.removeAttr('disabled');
									}
								}
							});
						}
					} else {
						layer.msg("未选择有效的Excel文件,无法上传!");
					}
				});
				
				/**
				 * 解析Excel数据并封装成对象
				 * @param {Object} file 文件对象
				 */
				function readExcelFromLocalFile(file) {
					let xlsReader = new FileReader();
					xlsReader.onload = function(e) {
						let data = e.target.result;
						// 读取Excel数据,按照BinaryString格式进行读取,type属性的其余可选值:base64、string(UTF-8编码的字符串)、buffer(Node.js中的Buffer类)、array(8位无符号整型数组)、file(仅在Node.js中支持)
						let workbook = XLSX.read(data, {
							type: 'binary'
						});
						// 获取当前Excel中第一张工作表的表名
						let sheetName = workbook.SheetNames[0];
						// 获取第一张工作表对象
						let sheet = workbook.Sheets[sheetName];
						const attrName = '!ref';
						// 获取第一张工作表的内容对象(即工作表对象中 !ref 属性的值)
						let ref = sheet[attrName];
						// 使用正则匹配表格数据
						let refArr = /([A-Z]+)([0-9]+):([A-Z]+)([0-9]+)/i.exec(ref);
						if (refArr != null) {
							// 定义表头行数量
							const HEAD_LEN = 2;
							// 遍历有效数据行
							for (let i = HEAD_LEN + 1; i <= refArr[4]; i++) {
								try{
									let item = {
										id: sheet['A' + i].v, // 序号
										name: sheet['B' + i].v, // 姓名
										gender: sheet['C' + i].v, // 性别
										age: sheet['D' + i].v, // 年龄
										phone: sheet['E' + i].v // 联系方式
									};
									datas.push(item);
								}catch(e){
									console.log("解析遇到单元格为空!第【" + i + "】行数据解析失败!");
								}
							}
							// 重载数据表格,参数一为表格渲染方法中id属性的值
							table.reload('dataTable', {
								data: datas
							}, false);
						} else {
				            layer.msg("该工作表无有效数据!");
				        }
					};
					// 按照BinaryString格式读取Excel文件
					xlsReader.readAsBinaryString(file);
				}
				
				/**
				 * 定义文件控件变更事件处理方法
				 * @param {Object} e 事件对象
				 */
				function handleFile(e) {
					let files = e.target.files;
					readExcelFromLocalFile(files[0]);
				}
				
				/**
				 * 添加事件句柄
				 */
				function addEventListener() {
					let uploadFile = $('#uploadFile');
					if (uploadFile) {
						uploadFile.on('change', handleFile);
					}
				}
				addEventListener();
			});
		</script>
	</body>
</html>

尾声

这是本人是第一次写博客,虽说平常也会写写技术总结,但毕竟只是给自己看的,这次直接将自己写的文档面向网络上所有用户,心理还是挺忐忑的。还望各位多多指教,帮我指出其中错误或不足之处,同时也希望这篇文章能帮到大家。最后,非常感谢sheetjs、layui等项目的开发者们,使我们可以仅用少量代码就能完成复杂功能,谢谢!

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部