标签 fastadmin 下的文章

先贴代码

【JS部分】

index:function(){
        //*************************** 自定义export开始
        $(document).on("click", "#btn-export-all", function () {
            //当前窗口
            var index;
            var options = table.bootstrapTable('getOptions');
            var search = options.queryParams({});
            //拼接参数
            var export_url = "card/orders/export";
            var post_url = export_url + '?' + Object.keys(search).map(i => `${i}=${search[i]||''}`).join('&')
            //请求导出
            layer.confirm('确定导出?',{btn: ['确定', '取消'],title:"提示"}, function(){
                    //设置等待5s
                    layer.load(0,{time:5000});
                    //关闭窗口
                    layer.close(layer.index);
                    //执行下载
                    $.ajax({
                    type: "post",
                    url: post_url,
                    data: null,
                    dataType: "json",
                    async:true,
                    success:function(data) {
                            if(data.code == 1){
                                // 创建a标签,设置属性,并触发点击下载
                                var $a = $("<a>");
                                $a.attr("href", data.file);
                                $a.attr("download",data.filename);
                                $("body").append($a);
                                $a[0].click();
                                $a.remove();
                                layer.msg('导出成功', {icon: 1});
                            }else{
                                layer.msg('导出失败', {icon: 2});
                            }
                        }
                    });
                });
        });
        //*************************** 自定义export结束
}

【控制器部分】


/**
 * 导出功能
 */

 public function export()
{
       $this->relationSearch = true;
        if ($this->request->isPost()) {
            set_time_limit(0);
            ini_set("memory_limit", -1); //-1不限制内存

            $xlsName =date('YmdHis').'订单数据';
            //设置过滤方法
            $this->request->filter(['strip_tags', 'trim']);

            //如果发送的来源是Selectpage,则转发到Selectpage
            if ($this->request->request('keyField')) {
                return $this->selectpage();
            }
            list($where, $sort, $order, $offset, $limit) = $this->buildparams();

            $total = $this->model
                ->with(['merchandise','lists','user'])
                ->where($where)
                ->order($sort, $order)
                ->count();

            $list = $this->model
                ->with(['merchandise','lists','user'])
                ->where($where)
                ->order($sort, $order)
                ->limit($offset, $limit)
                ->select();

            $order_status_lists=['-1'=>'已退货','1'=>'待发货','2'=>'发货中','3'=>'已发货'];
            $pay_status_lists=['-1'=>'支付失败','1'=>'待支付','2'=>'已支付'];
            $process_status_lists=['-1'=>'处理失败','1'=>'未处理','2'=>'待处理','3'=>'已处理'];

            #数据重组
            foreach ($list as $row) {
                  $row['store_name']=$row['lists']['name'];
                  $row['order_status_txt']=isset($order_status_lists[$row['order_status']])?$order_status_lists[$row['order_status']]:'';
                  $row['pay_status_txt']=isset($pay_status_lists[$row['pay_status']])?$pay_status_lists[$row['pay_status']]:'';
                  $row['process_status_txt']=isset($process_status_lists[$row['process_status']])?$process_status_lists[$row['process_status']]:'';
                  $row['create_time_txt']=!empty($row['create_time'])?date('Y-m-d H:i:s',$row['create_time']):'';
                  $row['pay_time_txt']=!empty($row['pay_time'])?date('Y-m-d H:i:s',$row['pay_time']):'';
                  $row['process_time_txt']=!empty($row['process_time'])?date('Y-m-d H:i:s',$row['process_time']):'';
                  $row['refund_time_txt']=!empty($row['refund_time'])?date('Y-m-d H:i:s',$row['refund_time']):'';
            }
             $xlsCell = [
                ['card_order_no', '订单编号'],
                ['title', '订单标题'],
                ['store_id', '网点Id'],
                ['user_id', '用户ID'],
                ['card_merchandise_id', '卡券商品ID'],
                ['store_name', '店铺名称'],
                ['mobile', '手机号'],
                ['card_type', '卡券类型'],
                ['coupons_no', '卡券编号'],
                ['original_price', '原价'],
                ['shop_price', '售价'],
                ['subsidy_price', '补贴价'],
                ['coupon_price', '优惠价格'],
                ['number', '数量'],
                ['pay_price', '支付金额'],
                ['pay_type', '支付类型'],
                ['pay_status_txt', '支付状态'],
                ['order_status_txt', '订单状态'],
                ['process_status_txt', '处理状态'],
                ['pay_time_txt', '支付日期'],
                ['process_time_txt', '处理日期'],
                ['refund_time_txt', '退款日期'],
                ['refund_reason', '退款原因'],
                ['refund_remark', '退款信息标注'],
                ['create_time_txt', '创建日期']
              ];

          return $this->downloadExcel($xlsName,$xlsCell,$list);    
        }
        
        
}

■注意:不要生搬硬套!我的控制使用了联查

【导出的类】可放在controller里面

    protected function downloadExcel($expTitle, $expCellName, $expTableData)
{
    $xlsTitle    = iconv('utf-8', 'gb2312', $expTitle);//文件名称
    $fileName    = $expTitle;
    $cellNum     = count($expCellName);// 单元格长度
    $dataNum     = count($expTableData);
    $spreadsheet = new Spreadsheet();
    $worksheet = $spreadsheet->getActiveSheet();
    $worksheet->setTitle($expTitle);

    $cellName = [
        'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 
        'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z','AA','AB','AC','AD','AE','AF','AG','AH','AI','AJ','AK','AL','AM','AN','AO','AP','AQ','AR','AS','AT'
    ];
    $spreadsheet->getActiveSheet(0)
        ->mergeCells('A1:' . $cellName[$cellNum - 1] . '1');//合并单元格为表头
    $spreadsheet->setActiveSheetIndex(0)->setCellValue('A1', $expTitle);// 设置表头单元格
    for ($i = 0; $i < $cellNum; $i++) {
        $spreadsheet->setActiveSheetIndex(0)
        ->setCellValue($cellName[$i] . '2', $expCellName[$i][1]);
    // 设置列
    }
    // Miscellaneous glyphs, UTF-8  循环写入数据
    for ($i = 0; $i < $dataNum; $i++) {
        for ($j = 0; $j < $cellNum; $j++) {
            $item_index =$expTableData[$i][$expCellName[$j][0]];
            if(is_numeric($item_index)&strlen($item_index)>9){
              $spreadsheet->getActiveSheet(0)->setCellValue($cellName[$j] . ($i + 3),' '.$item_index);
            }else{
              $spreadsheet->getActiveSheet(0)->setCellValue($cellName[$j] . ($i + 3),$item_index);
            }
        }
    }
    $end_filename =$fileName.'.xls';
    #ob_end_clean();
    //这一步非常关键,用来清除缓冲区防止导出的excel乱码
    header('pragma:public');
    header('Content-type:application/vnd.ms-excel');
    //header('Content-type:application/vnd.ms-excel;charset=utf-8;name="' . $xlsTitle . '.xls"');
    header("Content-Disposition:attachment;filename=$fileName.xls");
    header('Cache-Control: max-age=0');
    //"xls"参考下一条备注
    //$objWriter = \PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel5');
    $writer = new Xlsx($spreadsheet);
    //"Excel2007"生成2007版本的xlsx,"Excel5"生成2003版本的xls 调用工厂类
    $writer->save('php://output');
    $xlsData = ob_get_contents();
    ob_end_clean();
    return json(['code'=>1,'filename'=>$end_filename,'file'=>'data:application/vnd.ms-excel;base64,'.base64_encode($xlsData)]);
}

【特别声明】

use PhpOffice\PhpSpreadsheet\Helper\Sample;
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\Worksheet\PageSetup;
use PhpOffice\PhpSpreadsheet\Cell\DataType;
use PhpOffice\PhpSpreadsheet\Style\Fill;
use PhpOffice\PhpSpreadsheet\Style\Color;
use PhpOffice\PhpSpreadsheet\Style\Alignment;
use PhpOffice\PhpSpreadsheet\Style\Border;
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;

【视图部分】

index.html

增加导出按钮

   <a href="javascript:;" class="btn btn-warning" title="导出全部" id="btn-export-all"><i class="fa fa-send"></i> 全部导出</a>

composer安装

composer require topthink/think-worker=1.0.*

4.png

workman服务需要PHP开启某些函数

//使用workerman需要解除以下函数的禁用

stream_socket_server
stream_socket_client
pcntl_signal_dispatch
pcntl_signal
pcntl_alarm
pcntl_fork
posix_getuid
posix_getpwuid
posix_kill
posix_setsid
posix_getpid
posix_getpwnam
posix_getgrnam
posix_getgid
posix_setgid
posix_initgroups
posix_setuid
posix_isatty

在项目根目录创建

server.php

代码如下

<?php
define('APP_PATH', __DIR__ . '/application/');
define('BIND_MODULE','push/Worker');
require __DIR__ . '/thinkphp/start.php';

*创建模块应答

application|push|controller|Worker.php

代码如下

<?php    
namespace app\push\controller;    
use think\worker\Server;
use think\Log;
use think\Controller;
class Worker extends Server
{
    protected $socket = 'websocket://0.0.0.0:2346';    
    /**
     * 收到信息
     * @param $connection
     * @param $data
     */
    public function onMessage($connection, $data)
    {
        $params =$data;
        $data = json_decode($data,true);
        if ($data['sign']){
            $connection->send($data['sign']);
        }else{
            $return_data ="我于".date("Y-m-d H:i:s")."收到您的信息:".$params;
            $connection->send($return_data);
        }
    }    
    /**
     * 当连接建立时触发的回调函数
     * @param $connection
     */
    public function onConnect($connection)
    {    
    }    
    /**
     * 当连接断开时触发的回调函数
     * @param $connection
     */
    public function onClose($connection)
    {            
    }    
    /**
     * 当客户端的连接上发生错误时触发
     * @param $connection
     * @param $code
     * @param $msg
     */
    public function onError($connection, $code, $msg)
    {
        echo "error $code $msg\n";
    }    
    /**
     * 每个进程启动
     * @param $worker
     */
    public function onWorkerStart($worker)
    {    
        Log::write('进程启动了:'.date('Y-m-d H:i:s'),'info');
    }

    public function index(){
        parent::start();
    }
}

启动php服务进程
78.png

php  server.php start #启动 |stop 停止|restart重启

Nginx配置反向代理

在server内添加

location /wss {
          proxy_pass http://127.0.0.1:2346/;
          proxy_http_version 1.1;
          proxy_set_header Upgrade $http_upgrade;
          proxy_set_header Connection "upgrade";
          proxy_set_header Host $host;
          proxy_set_header X-Real-IP $remote_addr;
          proxy_set_header REMOTE-HOST $remote_addr;
          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
          client_max_body_size 5000M;
  }

客户端访问代码
我本地创建了一个8.html文件访问

<!doctype html>
<html lang="en">
 <head>
  <meta charset="UTF-8">
  <title>测试接收数据</title>
  <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
 </head>
 <body>
 <button type="button" class="signScore">点击一下</button>
  <script type="application/javascript">
  function StartWebSocket(){
    websocket = new WebSocket("ws://app.dev.ejiegd.com/wss");
    websocket.onopen = function (data) {
      // 发送信息
      var content =Math.random().toString(36).slice(2,10);
      websocket.send(content);
    };
    websocket.onmessage = function (data) {
      console.log("接收内容");
      alert(data.data);
      console.log(data.data); // 接收信息
    };
    websocket.onclose = function (data) {
      console.log("关闭", data); //关闭
    };
 }
    $(".signScore").click(function () {
        StartWebSocket();
    })
</script>
 </body>
</html>

最终效果

6.png

workerman可以一直运行,以daemon(守护进程)方式启动workerman即可后台一直运行。

启动停止workerman:

启动

以debug(调试)方式启动

php server.php start

以daemon(守护进程)方式启动

php server.php start -d

停止

php server.php stop

重启

php server.php restart

平滑重启

php server.php reload

查看状态

php server.php status

debug和daemon方式区别:

1、以debug方式启动,代码中echo、var_dump、print等打印函数会直接输出在终端。

2、以daemon方式启动,代码中echo、var_dump、print等打印会默认重定向到/dev/null文件,可以通过设置Worker::$stdoutFile = '/your/path/file';来设置这个文件路径。

3、以debug方式启动,终端关闭后workerman会随之关闭并退出。

4、以daemon方式启动,终端关闭后workerman继续后台正常运行。

最近在使用fastadmin框架(tp5.1)实现后台的基本功能,在selectpage需要显示更多信息,根据文档信息进行改造实现以下功能:

A-1.png

分别在控制器、以及view文件进行改造。

controller 控制器代码部分

#增加显示更多字段
protected $selectpageFields = "id,batch_number,coupons_date,coupons_money,coupons_remain";

View 视图部分 本实例在add.html

                <div class="col-xs-12 col-sm-8">
                    <input id="c-batch_number" data-rule="required" data-source="Batch/selectpage" class="form-control selectpage" name="row[batch_number]" type="text" value="" data-primary-key="batch_number" data-field="batch_number" data-format-item="{batch_number}-(有效期:{coupons_date}-剩余:{coupons_remain})">
                </div>

记录之,方便后期使用。

如出现乱码,请以utf-8文件编码保存

controller

/**

  • 自定义selectpage
    */

public function select_goods(){

list($where, $sort, $order, $offset, $limit) = $this->buildparams();
$list = $this->model
            ->with(['goodslists'])
            ->field('goodslists.name as name,goods_id')
            ->where($where)
            ->order($sort, $order)
            ->paginate($limit);
    $result = array("total" => $list->total(), "rows" => $list->items());
    return json($result);

}

model

public function goodslists()
{

return $this->belongsTo('app\admin\model\gascard\Goods', 'goods_id', 'id', [], 'LEFT')->setEagerlyType(0);

}

        {field: 'operate', title: __('Operate'), 
                        table: table, 
                        events: Table.api.events.operate, formatter: function (value, row, index) {
                            var that = $.extend({}, this);
                            var table = $(that.table).clone(true);
                            if (row.is_stock==1){
                            $(table).data("operate-del", null);
                            $(table).data("operate-edit", null);
                            }
                            that.table = table;
                            return Table.api.formatter.operate.call(that, value, row, index);
                        }}

以上是根据is_stock=1隐藏