Laravel Excel 结合 Ajax 方式实现导出功能

最近在做一个导出Excel的功能,经过两天的努力,终于算是搞定了~
要求:
1.同页面友好提示
2.无数据禁止下载
网上已经有很多类似的教程,这里就不啰嗦了,重点介绍一下,“下载中...”弹出框的实现。
这里使用了bootstrap和amazeui插件,以及jquery.fileDownload.js,下面是代码实现:
前端:

<html>
    <head>
        ...
        ...
        ...
        <link rel="stylesheet" href="http://cdn.amazeui.org/amazeui/2.7.2/css/amazeui.css">
        <script src="http://cdn.amazeui.org/amazeui/2.7.2/js/amazeui.js"></script>
        <script src="https://cdn.bootcss.com/jquery.fileDownload/1.4.2/jquery.fileDownload.js"></script>
    </head>
    <body>
        <a href="javascript:void(0);" onclick="fileDown();">导出</a>
        {{--询问框begin--}}
        <div class="am-modal am-modal-confirm" tabindex="-1" id="my-confirm">
            <div class="am-modal-dialog">
                <div class="am-modal-bd tk_title">
                    内容
                </div>
                <div class="am-modal-footer">
                    <span class="am-modal-btn" data-am-modal-cancel>取消</span>
                    <span class="am-modal-btn" data-am-modal-confirm>下载账单</span>
                </div>
            </div>
        </div>
        {{--询问框end--}}

        {{--弹出层begin--}}
        <div class="am-modal am-modal-alert" tabindex="-1" id="my-alert">
            <div class="am-modal-dialog">
                <div class="am-modal-hd">Amaze UI</div>
                <div class="am-modal-bd">
                    Hello world!
                </div>
                <div class="am-modal-footer">
                    <span class="am-modal-btn">确定</span>
                </div>
            </div>
        </div>
        {{--弹出层end--}}

        {{--加载中begin--}}
        <div class="am-modal am-modal-loading am-modal-no-btn" tabindex="-1" id="my-modal-loading">
            <div class="am-modal-dialog">
                <div class="am-modal-hd">正在载入...</div>
                <div class="am-modal-bd">
                    <span class="am-icon-spinner am-icon-spin"></span>
                </div>
            </div>
        </div>
        {{--加载中end--}}

        <script>
        // 询问框
        function fileDown() {
            var content = '确定要导出表格吗?'; // 提示内容
            $('.tk_title').text(content);
            $('#my-confirm').modal({ // 询问框id名称
                relatedTarget: this,
                onConfirm: function(options) {
                    downloading();
                },
                onCancel: function() {
                    //点击了取消
                }
            });
        }

        // 弹出框
        var modal = $('#my-alert');
        function my_alert(title, content, open) {
            modal.find('.am-modal-hd').html(title);
            modal.find('.am-modal-bd').html(content);
            if (open) {
                modal.modal();
            } else {
                modal.modal('close');
            }
        }

        // 加载框
        var modal_loading = $('#my-modal-loading');
        function my_loading(title, open) {
            modal_loading.find('.am-modal-hd').html(title);
            if (open) {
                modal_loading.modal();
            } else {
                modal_loading.modal('close');
            }
        }

        // 下载事件
        function downloading() {
            my_loading( '加载中,请稍后...', true);
            $.ajax({
                type: "POST",
                dataType: "json",
                data: {
                    _token: "{{ csrf_token() }}",
                    types: 2, //重要,体现在后端
                    // 其他参数
                },
                url: "{{ route('user.exports') }}",
                // 其他参数
                success: function (result, textStatus) {
                    if (result.status == 'success') {
                        my_loading("加载中,请稍后...", false);
                        $.fileDownload("{{ route('user.exports') }}", {
                            data: date,
                            prepareCallback: function (url) {
                                my_loading("正在下载,请稍后...", true);
                            },
                            successCallback: function (url) {
                                my_loading("正在下载,请稍后...", false);
                                my_alert("SUCCESS", "导出完成!", true);
                            },
                            failCallback: function (html, url) {
                                my_loading("正在下载,请稍后...", false);
                                my_alert("ERROR", "导出失败,未知的异常!", true);
                            }
                        });
                    } else {
                        my_loading("正在下载,请稍后...", false);
                        my_alert(result.msg, true);
                    }
                },
                error: function(XMLHttpRequest, textStatus, errorThrown) {
                    my_alert("ERROR", textStatus, true);
                }
            });
        }
        </script>
    </body>
</html>

基本功能实现,其他的可以根据实际情况作出相应修改,例如根据时间段导出,可以传参等。
后端代码:
UserController.php

<?php
namespace App\Http\Controllers\User;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Mockery\CountValidator\Exception;
use App\Models\User;

class UserController extends Controller
{
    public function exports(Request $request)
    {
        $type = $request->input('types', 1);

        $users = User::get();

        if (!$users->count()) {
            return json([
                'code' => '201',
                'status' => 'error',
                'msg' => '暂无数据',
            ]);
        }

        if ($types != 1) {
            return json([
                'code' => 200,
                'status' => 'success',
                'msg' => '导出成功'
            ]);
        }

        /**
         * 数据处理
         */

        $cellData['title'] = ['表头'];
        $cellData['columns'] = ['A1', 'B1', 'C1']; // 表头列名
        $cellData['body'] = $users;

        Excel::create(iconv('utf-8', 'gbk', '文件名'), function ($excel) use ($cellData) {
            $excel->sheet('score', function ($sheet) use ($cellData) {
                // 表头插入
                $sheet->appendRow(1, $cellData['title']);
                $sheet->appendRow(2, $cellData['columns']);

                // 填充数据
                $sheet->rows($cellData['body']);

                // 设置列宽
                $sheet->setWidth(array(
                     'A1" => '50px',
                     'B1" => '100px',
                     'C1" => '150px',
                ));

                // 冻结表头
                $sheet->setFreeze('A3');

                // 横向 合并单元格 A1 到 C1
                $sheet->mergeCells("A1:C1");

                // 纵向合并
                $sheet->setMergeColumn(array(
                    'columns' => array('B'),  // 列数
                    // 行数,一个二维数组
                    'rows' => array(
                        array(3, 5),
                        array(6, 14),
                        array(15, 23),
                        ...
                        ...
                        ...
                    )
                ));

                // 字体居中
                $sheet->cells("A1:C23", function ($cells) {
                    $cells->setAlignment('center');
                    $cells->setValignment('center');
                });
            });
        })->export('xls', ['Set-Cookie' => 'fileDownload=true; path=/']);
    }
}

到这里,基本就算完成了,其中最重要的就是 export() 中的 ['Set-Cookie' => 'fileDownload=true; path=/'], 这关系到前端 $.fileDownload 判断是否下载成功的标志,本来是怎么都搞不定,还是看了源代码的结果,才注意到的。
有需要的小伙伴,可以参考一下,下面是效果图:
询问框:
file
加载框:
file
成功下载:
file
失败下载:
file
至此就搞定了。
第一次写,有些啰嗦,见谅。。。

本帖已被设为精华帖!
本帖由系统于 5年前 自动加精
coldwinters
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
讨论数量: 15
ThinkQ

很好

5年前 评论

性能怎么样啊? 可以快速导什么量级的.

5年前 评论
coldwinters

@Teles 具体多少量级没有详细测试,我做的项目,因为要关联三张表,30天的数据,时间约在5秒内,如果要测试大量数据,你可以试一下

5年前 评论

还可以,收藏了

5年前 评论

哥好多错误啊。不能正常运行 :sob:

5年前 评论
coldwinters

@Asa_c 哪里报错呢?

5年前 评论

@OneLight 首先后端代码缺了一些括号。补上括号后 前端页面需要再引入一个jquery 引入后还是报错 500

5年前 评论
coldwinters

@Asa_c 后端括号已改正,谢谢指正! 有些地方的省略号,是要加入自己的东西,比如jquery/css等的引入,再排查一下看看

5年前 评论
coldwinters

@Asa_c 按照提示, 明显是你的data不存在啊!

file

5年前 评论

@OneLight 哥你这个歧义太多 照着你这个教程 = = 你确定可以直接用json 后台 不是 response()->json()

5年前 评论
coldwinters

@Asa_c 你要是有好方法, 也可以指导一下我嘛, 我在网上找了好久, 都没找到, 最后还是拼凑起来, 基本不影响功能实现才记录下来的.

5年前 评论

我加了fileDownload=true;path=/ 一样不触发successCallback 回调是怎么回事 :joy:

2年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!