效果如上,结合layer组件以及ajax分页实现,具体代码如下
前端代码
<a href="javascript:;" onclick="exportData()">用户数据导出</a>
// 执行事件
function exportData() {
var loading = layer.load(1, {
shade: [0.4,'#000']
}); // 弹出loading层
var formData = $(".layui-form").serialize(); // 获取搜索表单数据
$.post("{:url('download')}",formData,function (res) {
layer.close(loading);
if(res.code == 1){
doConfirm(res.data.total); // 弹出下载确认
}else{
layer.msg('没有可导出数据~');
}
})
}
/**
* 返回可执行数据后分页处理
* num 数据总数
**/
function doConfirm(num) {
if(num == 0){
layer.msg('没有可导出数据');
return ;
}
layer.confirm('共有'+num+'个用户数据需要导出,导出时间可能会较长,确认执行吗?',function () {
layer.closeAll();
layer.open({
type: 1, title: false, area: ['560px', '135px'], anim: 2, shadeClose: false, end: function () {
doAjax = false;
}, content: '' +
'<div class="padding-30 padding-bottom-0" style="width:500px" data-export-load>' +
' <div class="layui-elip nowrap" data-message-title>数据导出进度</div>' +
' <div class="margin-top-15 layui-progress layui-progress-big" lay-showPercent="yes"><div class="layui-progress-bar transition" lay-percent="10.00%"></div></div>' +
' <div class="margin-top-15" data-load-tips>正在导出,请等待,请勿关闭窗口,否则导出失败~~</div>' +
'</div>'
});
var pageNum = 1;
(function loadprocess(page,that) {
that = this,this.$box = $("[data-export-load]");
this.$area = that.$box.find('textarea'), this.$title = that.$box.find('[data-message-title]');
this.$percent = that.$box.find('.layui-progress div');
this.setState = function (status, message) {
if (status === 1) { // 已完成
that.$title.html('<b class="color-text">' + message + '</b>').addClass('text-center');
that.$percent.addClass('layui-bg-blue').removeClass('layui-bg-green layui-bg-red');
} else if (status === 2) {
if (message.indexOf('>>>') > -1) {
that.$title.html('<b class="color-blue">' + message + '</b>').addClass('text-center');
} else {
that.$title.html('<b class="color-blue">正在处理:</b>' + message).removeClass('text-center');
}
that.$percent.addClass('layui-bg-blue').removeClass('layui-bg-green layui-bg-red');
} else if (status === 3) { // 已完成
that.$title.html('<b class="color-green">' + message + '</b>').addClass('text-center');
that.$percent.addClass('layui-bg-green').removeClass('layui-bg-blue layui-bg-red');
}
};
var formData = $(".layui-form").serialize() + "&page="+pageNum; // 表单数据加 当前分页数
$.post("{:url('downpage')}",formData,function (res) {
that.setState(parseInt(res.code), res.message); // 返回数据
that.$percent.attr('lay-percent', (parseFloat(res.data.progress || '0.00').toFixed(2)) + '%'), layui.element.render(); // 更新进度条
if(res.code == 1){ // 进行中
pageNum = res.data.page;
setTimeout(function () {
loadprocess(pageNum); // 循环执行
},2000);
}else if(res.code == 3){ // 已完成
var downLink = "<a href='"+res.data.downurl+"' target='_blank'>《~点我下载~》</a>";
$("[data-load-tips]").html('导出完成,请点击下载保存至本地;'+downLink);
}
})
})(num);
});
}
后端代码,以Thinkphp为例
1、安装phpspreadsheet
composer require phpoffice/phpspreadsheet
2、逻辑代码,实现分页处理以及返回执行进度
<?php
namespace app\index\controller;
use lib\Dir;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
class Index {
/**
* 返回当前需导出数据总数
* @return json
**/
public function download(){
if(!$this->request->isAjax()){
return 'error';die;
}
$param = $this->request->param();
$name = input('name','');
$where[] = ['parent_id', '=','1'];
$query = $this->app->db->name('member');
$total = $query->where($where)->count();
return json(['code'=>1,'data'=>['total'=>$total]]);
}
/**
* 执行数据分页处理
*/
public function downpage(){
$param = $this->request->param();
// 创建存储目录
$excelPath = $this->app->getRootPath()."public/excel/excel_".$this->user['id'].DIRECTORY_SEPARATOR;
$zipPath = $this->app->getRootPath()."public/excel/";
if(!is_dir($excelPath)){
mkdir($excelPath,0775);
}
// 导出用户
$page = $param['page'];
$query = $this->app->db->name('member');
$total = $query->where($where)->order('create_at desc')->count();
$limit = 2; // 每次执行2条
$totalSize = ceil($total / $limit);
$progress = ceil($page * 100 / $totalSize); // 执行进度
if($page < $totalSize){
$list = $query->where($where)->page((int)$page,(int)$limit)->select()->toArray();
// 执行数据excel数据写入操作
$this->exp_userDetail($list,"某某名下会有数据",$excelPath);
//
return json(['code'=>1,'data'=>['progress'=>$progress,'page'=>$page+1],'message'=>'>>>正在导出数据中,请勿关闭窗口<<<']);
}
if($progress >= 100){
// 把所有文件生成压缩包
$zipName = $this->userData['nickname'].'_'.date('YmdHi').'_报表数据.zip';
$this->createZip($excelPath, $zipPath.$zipName);
// 删除所生成文件夹
$dir = new \lib\Dir();
$dir->delAll($excelPath);
return json(['code'=>3,'data'=>['progress'=>$progress,'page'=>$page,'downurl'=>"/excel/".$zipName],'message'=>'>>>导出完成<<<']);
}
}
/**
* 生成压缩包
* @param $path
* @param $zipName
*/
protected function createZip($path,$zipName){
$zip = new \ZipArchive();
if($zip->open($zipName,\ZipArchive::CREATE) === true){
$dir = opendir($path);
while ($file = readdir($dir)){
if(is_file($path.$file)){
$zip->addFile($path.$file,$file);
}
}
$zip->close();
}
}
/**
* 数据写入excel文件
* @param $list Array 数据集
* @param $name String 生成文件名
* @param $path String 文件生成路径
*/
protected function exp_userDetail($list,$name,$path){
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$title = ['姓名', '手机号码'];
$data = [];
foreach($list as $vo){
$data[] = [
'nickname'=> $vo['nickname'],
'phone'=> $vo['phone'],
];
}
foreach ($title as $key => $value) {
// 单元格内容写入
$sheet->setCellValueByColumnAndRow($key + 1, 1, $value);
}
$row = 2; // 从第二行开始
foreach ($data as $item) {
$column = 1;
foreach ($item as $value) {
// 单元格内容写入
$sheet->setCellValueByColumnAndRow($column, $row, $value);
$column++;
}
$row++;
}
$writer = new Xlsx($spreadsheet);
// 保存数据
$writer->save($path.$name.'_详细报表.xlsx');
}
}