基于layui2和thinkphp5项目开发心得记录(一)

写这系列的文章目的无他,仅仅是用以记录自己写项目的一些思路和心得,在开发过程中代码是以实现为首要目的,写得并不会非常美观实用,代码会在后期不断调试更改,文章也会进行不定期更新。未经作者允许请勿转载。

最新更新:2018/7/26

功能需求:选取页面表格信息,批量生成word文件 

相关技术点

  • 前端layui表格的数据显示与后台json接口
  • 页面与页面之间的参数传递,页面与后台之间的参数传递
  • php批量生成word文件(数组循环遍历、字符串与数组转化、js中window.location.search的用法和作用。)
  • html样式对word影响
  • 浏览器文件下载

首先是用一个layui表格对数据进行展示,html页面写个table标签,给个id

 按照layui框架的规则,在JS中对表格的表头值进行规定,field是与数据库对应的字段值,title是表格的表头

layui.use('table', function(){
  var table = layui.table;
  
  //表格列表
  var show_col=[[
            {type:'checkbox', fixed: 'left'}
            ,{field:'id',width:85, sort: 'true',  title: 'ID',style:"font-size:10px;"}
            ,{field:'name',width:180, sort: 'true',  title: '名称',style:"font-size:12px"}
            ,{field:'desc', width:180, sort: 'true' , title: '备注',style:"font-size:12px"}
            ,{field:'is_on_duty' ,width:140,sort: 'true', title: '是否管理',style:"font-size:12px"}
            ,{field:'status', width:94, sort: 'true' , title: '状态',style:"font-size:12px"}
            ,{fixed:'right', width:114, align:'center', toolbar: '#op-bar' , title: '操作',style:"font-size:12px"}
        ]];

  //表格属性
  var options={
    elem: '#projtb'
    ,height: 315
    ,url: ' '     //数据接口
    ,page: true     //开启分页
    ,cols: show_col
  }
 
  //渲染
  table.render(options);
  
});

请求后台接口,对表格数据进行渲染。数据来源都是从服务器上的数据库获取,感觉刚上手thinkphp5框架,对数据库的操作还是蛮顺手的。接口以json形式返回。注意layui表格的接口数据有它自己的默认规则(除非是自己定义返回的数据格式),如果只按照自己后台数据去定义json,会导致页面报错说接口异常,或者表格一片空白。默认接口数据格式以及自定义数据格式的官方文档传送门

/*数据接口*/
    public function getDeptList($keyword = '', $page = 1,$limit=20)
    {
        $map = [];
        if ($keyword) {
            $map['name'] = ['like', "%{$keyword}%"];
        }
        $dept_list_query = $this->dept_model->where($map)->order('id Asc')->paginate($limit, false, ['page' => $page]);
        $dept_list_arr = $dept_list_query->toArray();
        //json数组
        $arr = array();
        $arr['code'] = 0;
        $arr['msg'] = "";
        $arr['count'] = $dept_list_arr['total'];
        $arr['per_page'] = $dept_list_arr['per_page'];
        $arr['current_page'] = $dept_list_arr['current_page'];
        $arr['data'] = $dept_list_arr['data'];
        $arr_json = json($arr);
        return $arr_json;

    }

/* 默认接收的数据格式
    {
      code: 0,
      msg: "",
      count: 1000,
      data: []
    } 
*/

功能的流程大概思路是:选取需要生成word的项目,勾选第一列的复选框,然后点击页面的生成按钮,弹出一个窗口,进行模板选择(因为需求中有三个word模板),选择了模板之后,跳转新的页面,将生成的doc的链接展示出来,供用户点击下载。

流程清楚之后,接下来就是数据问题了,首先要获取到所选取的那些项目的对应id,还有模板选择那个页面的模板id,一起传进后台控制里的生成word的函数。因为在经过了两个前端html页面这些参数才传到后台,所以打算把第一个页面的项目id以数组的形式,利用url传参方式get到第二个模板选择页面,然后在第二个页面利用js中window.location.search获取到第一个页面传过来的项目id,再将项目id和模板id一起传入后台。

那么第一个页面如何获取到项目id呢?利用layui的表格监听,将用户勾选的每一条项目对应的数据存起来。再利用循环,将每条项目的项目id取出来存到一个新的数组,这个数组就是需要传到下一个页面的参数,里面包含了用户勾选到的项目的id,不说那么多,直接上代码。


    //JS代码
    layui.use(['form', 'layer'], function(){
        var $ = layui.jquery,
                layer = layui.layer,
                table = layui.table;

        //监听表格复选框选择
        table.on('checkbox(filtertb)', function(obj){
            var checkStatus = table.checkStatus('projtb');
            //将用户勾选的项目对应数据存起来
            clicked_project = checkStatus.data;
            //console.log(clicked_project);
        });

        var active = {
            //点击事件绑定
            exportWords:function () {
                //获取勾选的项目id,存入一个新数组,传入生成文档的子窗口
                var selected_project=new Array();
                for(var i=0;i0)
                {
                    layer.open({
                        shade: [0.8,'#000'],
                        shadeClose: true,
                        title: '请选择您要导出的模板文件',
                        type: 2,
                        area: ['460px', '500px'],
                        //向下个页面传入参数(已勾选的项目id)
                        content: '/chooseWordTemplate?pid='+selected_project,
                        btn: ['确定','取消']
                    });
                }else
                    {
                        layer.alert("您尚未选择需要导出word的项目!");
                    }

            }
        };

        //按钮触发
        $('.layui-btn').on('click', function(){
            var type = $(this).data('type');
            active[type] ? active[type].call(this) : '';
        });

    });
    /*
     * 控制器
     * 导出word模板选择
     * */
    public function chooseWordTemplate()
    {
        return $this->fetch('chooseWordTemplate');
    }

点击按钮之后,打开如下弹窗,进行模板选择

基于layui2和thinkphp5项目开发心得记录(一)_第1张图片

这是一个新的页面,就三个按钮,对应三个模板,利用data-type属性对每个按钮进行事件绑定


    

到此前台部分就完成了。选择模板之后,此时会往后台传入项目id的数组和模板id。控制器方法获取到项目id和模板id之后,便可以着手开始生成word模板了,在这所谓的生成word,其实说白了就是先写好模板的html表格代码,赋值到一个变量中,然后写入文件中,以doc为后缀进行保存。接下来就是批量生成的问题,因为前台传过来的项目id有很多个,而且是字符串,因此要先取出来,将每一个id存进数组里面,再利用foreach循环数组,查询每一个id对应的数据,然后赋值到模板,写入文件,保存,最后echo出链接,大概就是这么一个思路。下面是控制器代码:

    /*
     * 将项目导出至word,文件存放位置public\uploads\Word文件夹
     * $pid勾选项目的id值,$tep选择需要导出的Word模板id
     * */
    public function project2word($pid,$tep){
        $word = new word;
        //取出pid将其转成数组,进行循环遍历
        $pidArray = explode(",",$pid);
        echo
        "

生成完毕!请点击下载

"; foreach ($pidArray as $value) { $item = $this->getProject($value)->toArray(); /*表一*/ $data1 = ' Print 表一

****表

项目名称 '.$item['name'].'
预算金额(元) '.$item['budget_total'].' 申请部门 '.$item['dept_name'].'
备注
'; /*表二类似,代码省略*/ $data2 = ''; /*表三类似,代码省略*/ $data3 = ''; //以每天的日期为一个文件夹,当天生成的word会全部存在一个以日期命名的文件夹中 $str = date('Ymd'); //查询文件夹是否已经存在,如果不存在则创建 $targetFolder = iconv("UTF-8", "GBK", ROOT_PATH."public/uploads/Word/".$str); if (!file_exists($targetFolder)){ mkdir ($targetFolder,0777,true); } //取出tep,判断需要生成哪个模板 switch ($tep) { case 1: $name=ROOT_PATH."public/uploads/Word/".$str."/".$item['code']."tep1.doc"; $word->wirtefile($name,$data1); //保存word并且结束 //echo出的链接供给用户点击下载,链接href是word文件所在位置 echo "【" .$item['name']."】***表
"; break; case 2: $name=ROOT_PATH."public/uploads/Word/".$str."/".$item['code']."tep2.doc"; $word->wirtefile($name,$data2); //保存word并且结束. echo "【".$item['name']."】****表
"; break; case 3: $name=ROOT_PATH."public/uploads/Word/".$str."/".$item['code']."tep3.doc"; echo "【".$item['name']."】***表
"; $word->wirtefile($name,$data3); //保存word并且结束. break; } } }

上面控制器代码中引用了word类中的writefile方法,Word.php代码如下:

至此功能就已经大体实现了。但是这样做其实性能方面会没有那么好,而且给用户的体验不太友好,用户的整个操作流程下来会感觉不太方便,接下来会对此功能进行评测以及代码的改进。文章会不定期更新。

2018/7/25更新

出于对使用这一功能的人群考虑,实际使用中用户并不需要一个一个去修改,而是仅仅负责分发给其他人,因此将生成的word打包成压缩包供用户下载。所以决定在原来的基础上,将每一次的批量生成都压缩一下,将压缩包放在存放word的同级目录下,zip名以日期加上时分秒命名,实现起来也很简单,php有专门的ZipArchive类,代码参考:koastal的博客,里面写的很详细,下面是项目修改后的代码

    /*
     * 将项目导出至word,文件存放位置public\uploads\Word文件夹
     * $pid勾选项目的id值,$tep选择需要导出的Word模板id
     * */
    public function project2word($pid,$tep){
        $word = new word;
        //取出pid将其转成数组,进行循环遍历
        $pidArray = explode(",",$pid);
        echo
        "

生成完毕!请点击下载

"; //创建以当天日期为名的文件夹,用来存放当天所有生成的word $str = date('Ymd'); $targetFolder = iconv("UTF-8", "GBK", ROOT_PATH."public/uploads/Word/".$str); if (!file_exists($targetFolder)){ mkdir ($targetFolder,0777,true); } //定义zip文件名及路径,创建并打开zip通道 $zipStr = date('Ymdhis'); $zipFilename = ROOT_PATH.'public/uploads/Word/'.$str.'/'.$zipStr.'.zip'; $zip = new \ZipArchive(); $zip->open($zipFilename,\ZipArchive::CREATE); foreach ($pidArray as $value) { $item = $this->getProject($value)->toArray(); /*表一、二、三同上,代码省略*/ $data1 = ''; $data2 = ''; $data3 = ''; switch ($tep) { case 1: $name=ROOT_PATH."public/uploads/Word/".$str."/".$item['code']."tep1.doc"; $word->wirtefile($name,$data1); //保存word并且结束. $zip->addFile($name,basename($name)); //添加到zip echo "【" .$item['name']."】***表
"; break; case 2: $name=ROOT_PATH."public/uploads/Word/".$str."/".$item['code']."tep2.doc"; $word->wirtefile($name,$data2); //保存word并且结束. $zip->addFile($name,basename($name)); echo "【".$item['name']."】****表
"; break; case 3: $name=ROOT_PATH."public/uploads/Word/".$str."/".$item['code']."tep3.doc"; echo "【".$item['name']."】***表
"; $word->wirtefile($name,$data3); //保存word并且结束. $zip->addFile($name,basename($name)); break; } } //关闭压缩包 $zip->close(); echo "压缩包
"; }

压缩功能完成。

2018/7/26更新

之前在生成模板之前选择的项目id,是通过url参数get到下一个页面的,和下个页面的模板id一起get到后台。这么做虽然简单,但是也有一个致命的问题,就是当用户一次性选择项目过多时,比如几百条几千条,url就会变得很长,但是实际上浏览器对url长度是有上限规定的,因此利用get请求把参数传到下一个页面是不可能了。那不能用get请求,又不能把项目id先传到后台,因为后台函数需要同时接受项目id和模板id,怎么办? 一开始是想着能不能把模板id通过session存起来,在第二个页面访问。但是后来一想,用post不是更简单吗?可是转眼一想,不可能把项目id直接post到下一个页面吧?查了一下js是没办法获取post过来的数据的。好,那既然post不到下一个页面,那就看看layui有没有提供类似的功能。结果翻了一下官方文档,发现还真的有,就是在主页面利用layer.open打开的子窗口之间,是可以实现数据互通的,主页面可以访问子页面参数、方法,反之子页面也能访问主页面参数、方法。那既然如此,我何不在子页面去获取主页面的项目id,然后利用表单直接和模板id一起post到后台呢?锦上添花的是,layui提供了form表单的初始化赋值的功能,这么以来就省事多了,也不用去考虑主页面子页面谁去访问谁的东西了,直接在主页面的layer.open里面的success回调对子页面的表单进行赋值,完美解决了之前的各种问题,下面贴上部分修改后的代码。

主页面的layer.open以及对子窗口的表单初始化赋值:

        exportWords:function () {
                //获取勾选的项目信息,提取其中的项目id为一个新数组,方便之后传入生成文档的子窗口
                var selected_project=new Array();
                for(var i=0;i0)
                {
                    layer.open({
                        shade: [0.8,'#000'],
                        shadeClose: true,
                        title: '请选择您要导出的模板文件',
                        type: 2,
                        area: ['460px', '500px'],
                        //向下个页面传入参数(已勾选的项目id)
                        content: '__ROOT__/admin/project/chooseWordTemplate',
                        btn: '取消',
                        success: function (layero, index) {
                            //找到子窗口的DOM
                            var body = layui.layer.getChildFrame('body',index);
                            //利用对表单的值的初始化,将项目id传到子页面
                            body.find(".pid").val(selected_project);
                        }
                    });
                }else
                    {
                        layer.alert("您尚未选择需要导出word的项目!");
                    }

            }

子页面即chooseWordTemplate.html原来是三个按钮,现在直接重新写个表单,原来的三个按钮的模板选择变成了下拉框的模板选择,还有一个输入框,输入框里面的值是从父页面就已经初始化好的。chooseWordTemplate.html代码如下:




    
    选择word模板类型
    
    
    
    
    
    
    
    


页面效果如下(为了防止用户误操作,我把项目id的文本框禁用了,这里注意一下,layui的禁用是有两种,一种只是样式上的禁用,另一种是表单里禁用,即不会提交):

基于layui2和thinkphp5项目开发心得记录(一)_第2张图片

 然后是后台控制器代码修改:

    /*
     * 将项目导出至word,文件存放位置public\uploads\Word文件夹
     * $pid勾选项目的id值,$tep选择需要导出的Word模板id
     * */
    public function project2word(){
        $word = new word;
        $request = Request::instance();
/*      //取出pid
        $pidParam = $request->only('projid');   //获取参数是一个数组,注意是整体为一个value,所以要把相应value转成String类型,方便后面拆分再行转成数组
        $pid = implode($pidParam);      //数组$pidParam转成String类型的$pid    */
        //取出pid第二种方法
        $pidParam = $request->post('projid');       //这种获取方法直接就是string类型了
        $pidArray = explode(",",$pidParam);     //将其转成数组,进行循环遍历
        //取出tep,方便后面判断需要生成哪个模板
        $tepParam = $request->post('template');
        
        //后续操作不变...
    }

url过长问题到这就解决了。

你可能感兴趣的:(thinkphp,layui)