Browse Source

合并单元格

yzh 4 years ago
parent
commit
1acffbe369
2 changed files with 240 additions and 23 deletions
  1. 81 10
      examples/export_demo.php
  2. 159 13
      src/Pxlswrite.php

+ 81 - 10
examples/export_demo.php

@@ -13,7 +13,7 @@ $fileObj = new Pxlswrite(['path' => __DIR__ . '/uploads']);
 //实例化WebSocketClient--需要推送进度才实例化
 $pushHandle = new WebSocketClient('ws://192.168.18.192:9502', $_GET['fd']);
 //创建excel文件
-$fileObj->fileName('123.xlsx');
+$fileObj->fileName('1234.xlsx');
 //定义样式
 $style = [
     'align' => [Pxlswrite::FORMAT_ALIGN_CENTER, Pxlswrite::FORMAT_ALIGN_VERTICAL_CENTER],//对齐 [x,y]
@@ -37,16 +37,41 @@ $field = [
     'c4' => ['name' => 'll'],
     'c5' => ['name' => 'aa', 'callback' => 'myFormat']//callback 回调处理格式化值 可以是函数/对象方法
 ];
+$orderField =  [
+    'order'=>['name'=>'订单号'] ,
+    'time'=>['name'=>'下单时间'],
+    'username'=>['name'=>'用户名'],
+    'address'=>['name'=>'地址'],
+    'phone'=>['name'=>'手机号'],
+    'itemnumer'=>['name'=>'子订单号'],
+    'productname'=>['name'=>'商品名称'],
+    'amount'=>['name'=>'数量'],
+    'mark'=>['name'=>'备注'],
+];
 
 //注意:设置行与行/列与列样式 交集范围会覆盖;行样式优先于列样式
-$filePath = $fileObj->field($field)//设置字段&表格头
-    ->setDataByGenerator('generateData', $pushHandle)//设置数据 回调生成器方法获取数据,$pushHandle 用于推送,可不传
-    ->setRow('A1:A3', 80, $style)//设置范围行样式 80行高
-    ->setColumn('A:F', 20, ['background' => Pxlswrite::COLOR_GRAY])//设置范围列样式 20列宽
-    ->setRow('A1', 50, ['background' => Pxlswrite::COLOR_PINK, 'align' => [Pxlswrite::FORMAT_ALIGN_CENTER, Pxlswrite::FORMAT_ALIGN_VERTICAL_CENTER]])//设置指定某一行样式
-    ->setColumn('F:F', 60, ['background' => Pxlswrite::COLOR_YELLOW])//指定某一列样式
-    ->defaultFormat(['background' => Pxlswrite::COLOR_GREEN])//全局默认样式
-    ->mergeCells('A1:C1', 'Merge cells', ['align' => [Pxlswrite::FORMAT_ALIGN_CENTER, Pxlswrite::FORMAT_ALIGN_VERTICAL_CENTER]])//合并单元格
+/**
+ * 一般数据导出demo
+ */
+//$filePath = $fileObj->field($field)//设置字段&表格头
+//    ->setDataByGenerator('generateData', ['c1', 'c2'], ['align' => [Pxlswrite::FORMAT_ALIGN_CENTER, Pxlswrite::FORMAT_ALIGN_VERTICAL_CENTER]], $pushHandle)//设置数据 回调生成器方法获取数据,$pushHandle 用于推送,可不传
+//    ->setRow('A1:A3', 80, $style)//设置范围行样式 80行高
+//    ->setColumn('A:F', 20, ['background' => Pxlswrite::COLOR_GRAY, 'align' => [Pxlswrite::FORMAT_ALIGN_CENTER, Pxlswrite::FORMAT_ALIGN_VERTICAL_CENTER]])//设置范围列样式 20列宽
+//    ->setRow('A1', 50, ['background' => Pxlswrite::COLOR_PINK, 'align' => [Pxlswrite::FORMAT_ALIGN_CENTER, Pxlswrite::FORMAT_ALIGN_VERTICAL_CENTER]])//设置指定某一行样式
+//    ->setColumn('F:F', 60, ['background' => Pxlswrite::COLOR_YELLOW])//指定某一列样式
+//    ->mergeCells('A1:C1', 'Merge cells', ['align' => [Pxlswrite::FORMAT_ALIGN_CENTER, Pxlswrite::FORMAT_ALIGN_VERTICAL_CENTER]])//合并单元格
+//    ->defaultFormat(['align'=>[Pxlswrite::FORMAT_ALIGN_CENTER,Pxlswrite::FORMAT_ALIGN_VERTICAL_CENTER]])//全局默认样式
+//    ->output();//输出excel文件到磁盘
+
+/**
+ * 订单数据导出demo
+ */
+$filePath = $fileObj
+    ->defaultFormat(['align'=>[Pxlswrite::FORMAT_ALIGN_CENTER,Pxlswrite::FORMAT_ALIGN_VERTICAL_CENTER]])//全局默认样式
+    ->field($orderField)//设置字段&表格头
+    ->setOrderData('generateOrderData',['order','time'],[],$pushHandle)
+    ->setColumn('A:B',50)
+    ->setColumn('F:F',50)
     ->output();//输出excel文件到磁盘
 
 //单元格插入文本
@@ -69,7 +94,7 @@ function generateData()
 {
     $db = DB::getInstance();
     $step = 10000;
-    for ($i = 0; $i < 100000; $i = $i + $step) {
+    for ($i = 0; $i < 10000; $i = $i + $step) {
         yield $db->get_records_sql("select * from sheet1 limit {$i},{$step}", null, PDO::FETCH_ASSOC);
     }
 }
@@ -78,4 +103,50 @@ function generateData()
 function myFormat($v, $values)
 {
     return $v . '自定义格式化-' . $values['id'];
+}
+
+//数据生成器--获取订单数据,数据格式如下,字段数量名称不限,只支持一个item数组
+//$data = [
+//    [
+//        'order'=>'20200632555' ,
+//        'time'=>'2020-06-30 12:30:10',
+//        'username'=>'张三',
+//        'address'=>'成都',
+//        'phone'=>'17756891562',
+//        'item'=> [
+//            [
+//                'itemnumer'=>'2020515',
+//                'productname'=>'商品1',
+//                'mark'=>'备注',
+//            ],
+//        ],
+//    ]
+//];
+function generateOrderData(){
+    for ($i=0;$i<100;$i++){
+        $order = [];
+        $range = mt_rand(0,5);
+        for($k = 0;$k<$range;$k++){
+            $orderId = date('YmdHis').rand(1000,9999);
+            $range2 = mt_rand(0,5);
+            $item = [];
+            for($j = 0;$j<$range2;$j++){
+                $item[] = [
+                    'itemnumer'=>$orderId,
+                    'productname'=>'商品'.rand(10,99),
+                    'amount'=>'1',
+                    'mark'=>'备注',
+                ];
+            }
+            $order[] = [
+                'order'=>$orderId ,
+                'time'=>date('Y-m-d H:i:s'),
+                'username'=>'张三',
+                'address'=>'成都',
+                'phone'=>'17756891562',
+                'item'=> $item,
+            ];
+        }
+        yield $order;
+    }
 }

+ 159 - 13
src/Pxlswrite.php

@@ -74,6 +74,14 @@ class Pxlswrite extends Excel
      * @var array
      */
     public $header = [];
+    /**
+     * 单元格字段范围
+     */
+    const CELLRANGE = array('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');
+    /**
+     * @var resource 默认样式
+     */
+    public $m_defaultStyle;
 
     /**
      * Pxlswrite constructor.
@@ -146,28 +154,61 @@ class Pxlswrite extends Excel
     }
 
     /**
-     * 通过生成器按行向表格插入数据
-     * @param $_generator
+     * 通过生成器逐行向表格插入数据,
+     * 设置过field才支持动态单元格合并,
+     * 可以根据指定的字段 通过值比较自动进行 行合并
+     * @param $_generator 回调数据生成器方法 返回的数据格式是二维数组 如下字段名数量不限
+     * [['id'=>1,'name'=>'张三','age'=>'18']]
+     * @param array $_mergeColumn 需要合并的字段
+     * @param array $_mergeColumnStyle 合并单元格的样式
+     * @param int $_index 单元格行偏移量 合并单元格的起始位置
      * @param WebSocketClient $_pushHandle
      * @return Pxlswrite
+     * @throws \Exception
      */
-    public function setDataByGenerator($_generator, WebSocketClient $_pushHandle = null)
+    public function setDataByGenerator($_generator, array $_mergeColumn = [], array $_mergeColumnStyle = [], WebSocketClient $_pushHandle = null, $_index = 1)
     {
-        $count = 0;
-        //判断是否有定义字段
+        $count = 0;//统计数据处理条数
+        $cellKey = [];//装载需要合并的字段
+        $_mergeColumnStyle = !empty($_mergeColumnStyle) ? $_mergeColumnStyle : $this->m_defaultStyle;
+        foreach ($_mergeColumn as $k => $v) {
+            $key = array_search($v, array_keys($this->fieldsCallback));
+            $cellKey[$v] = self::CELLRANGE[$key];
+            //临时存放需要合并的值
+            $tempValue[$v] = [
+                'count' => 0,
+                'value' => null,
+            ];
+        }
+        //判断是否有定义字段 有则进行字段格式化&字段过滤
         if (!empty($this->fieldsCallback)) {
             foreach (call_user_func($_generator) as $item) {
                 foreach ($item as $value) {
-                    $temp = [];
-                    //字段过滤
-                    foreach ($this->fieldsCallback as $k => $v) {
-                        $temp[$k] = isset($value[$k]) && !empty($value[$k]) ? $value[$k] : '';
-                        //回调字段处理方法
-                        if (isset($v['callback'])) {
-                            $temp[$k] = call_user_func($v['callback'], $temp[$k], $value);
+                    $_index++;//单元行自增
+                    $temp = $this->filter($value);
+                    $this->data([array_values($temp)]);//二维索引数组
+                    //处理需要合并的单元格
+                    foreach ($cellKey as $c => $cell) {
+                        if ($tempValue[$c]['count'] == 0) {
+                            $tempValue[$c]['value'] = $temp[$c];
+                        }
+                        if ($temp[$c] == $tempValue[$c]['value']) {
+                            $cellMerge[$c][] = $cell . $_index;
+                            $tempValue[$c]['count']++;
+                        }
+                        //当前单元格与前一单元格值不在相等时合并单元格
+                        if ($temp[$c] != $tempValue[$c]['value']) {
+                            if (!empty($cellMerge[$c])) {
+                                $startPosition = $cellMerge[$c][0];
+                                $endPosition = end($cellMerge[$c]);
+                                $this->mergeCells($startPosition . ':' . $endPosition, $tempValue[$c]['value'], $_mergeColumnStyle);
+                            }
+                            $cellMerge[$c] = [];
+                            $tempValue[$c]['count'] = 1;
+                            $tempValue[$c]['value'] = $temp[$c];
+                            $cellMerge[$c][] = $cell . $_index;
                         }
                     }
-                    $this->data([array_values($temp)]);//二维索引数组
                 }
                 //推送消息
                 $count += count($item);
@@ -187,6 +228,110 @@ class Pxlswrite extends Excel
         return $this;
     }
 
+    /**
+     * 设置订单数据 根据数据可以合并指定的字段,需要遵循以下数据格式
+     * @param $_generator 数据生成器方法 返回数据格式如下,字段数量名称不限,只支持一个item二维数组
+     * [
+     *    [
+     *        'order'=>'20200632555' ,
+     *        'time'=>'2020-06-30 12:30:10',
+     *        'username'=>'张三',
+     *        'address'=>'成都',
+     *        'phone'=>'17756891562',
+     *        'item'=> [
+     *            [
+     *                'itemnumer'=>'2020515',
+     *                'productname'=>'商品1',
+     *                'mark'=>'备注',
+     *            ],
+     *        ],
+     *    ]
+     * ];
+     * @param array $_mergeColumn 需要合并的字段
+     * @param array $_mergeColumnStyle 合并单元格样式
+     * @param WebSocketClient|null $_pushHandle WebSocketClient对象 用于推送进度
+     * @param int $_index 单元格行偏移量 合并单元格的起始位置
+     * @return $this
+     * @throws \Exception
+     */
+    public function setOrderData($_generator, array $_mergeColumn = [], array $_mergeColumnStyle = [], WebSocketClient $_pushHandle = null, $_index = 1)
+    {
+        $count = 0;//统计数据处理条数
+        $cellKey = [];//装载需要合并的字段
+        $_mergeColumnStyle = !empty($_mergeColumnStyle) ? $_mergeColumnStyle : $this->m_defaultStyle;
+        foreach ($_mergeColumn as $k => $v) {
+            $key = array_search($v, array_keys($this->fieldsCallback));
+            $cellKey[$v] = self::CELLRANGE[$key];
+        }
+        foreach (call_user_func($_generator) as $item) {
+            foreach ($item as $key => $value) {
+                $i = 0;//标记数组指针位置
+                foreach ($value as $k1 => $v1) {
+                    $i++;
+                    //判断当前值是否是数组
+                    if (!is_array($v1)) {
+                        $orderInfo[$k1] = $v1;//存放order的信息
+                    } else {
+                        //是数组则进行遍历格式化值
+                        $temp = [];//存放处理后的item值
+                        if (!empty($v1)) {
+                            foreach ($v1 as $k2 => $v2) {
+                                $temp[] = $this->filter($v2);
+                            }
+                        } else {
+                            $temp[] = $this->filter([]);
+                        }
+                    }
+                    //遍历到数组最后一个时,进行逐行插入、合并单元格
+                    if (count($value) == $i) {
+                        //处理订单相关字段过滤
+                        $orderTemp = $this->filter($orderInfo);
+                        foreach ($orderTemp as $k4 => $v4) {
+                            if (!key_exists($k4, $orderInfo)) {
+                                unset($orderTemp[$k4]);
+                            }
+                        }
+                        //循环插入订单item  一个order对应多个item
+                        foreach ($temp as $k3 => $v3) {
+                            $_index++;//单元行自增
+                            $data = array_merge($v3, $orderTemp);
+                            $this->data([array_values($data)]);
+                        }
+                        //合并单元格
+                        foreach ($cellKey as $column => $cell) {
+                            $offset = $_index - count($temp) + 1;
+                            $startPosition = $cell . $offset;
+                            $endPosition = $cell . $_index;
+                            $this->mergeCells($startPosition . ':' . $endPosition, $orderTemp[$column], $_mergeColumnStyle);
+                        }
+                    }
+                }
+            }
+            //推送消息
+            $count += count($item);
+            $this->push($_pushHandle, $count);
+        }
+        return $this;
+    }
+
+    /**
+     * 字段过滤&格式化
+     * @param $value 一维数组
+     * @return array 处理之后的结果数组
+     */
+    public function filter($value)
+    {
+        $temp = [];
+        foreach ($this->fieldsCallback as $k => $v) {
+            $temp[$k] = isset($value[$k]) ? $value[$k] : '';
+            //回调字段处理方法
+            if (isset($v['callback'])) {
+                $temp[$k] = call_user_func($v['callback'], $temp[$k], $value);
+            }
+        }
+        return $temp;
+    }
+
     /**
      * 导入数据
      * @param $_func string 方法名 回调数据插入的方法
@@ -389,6 +534,7 @@ class Pxlswrite extends Excel
         if (!is_resource($formatHandler)) {
             $formatHandler = $this->styleFormat($formatHandler);
         }
+        $this->m_defaultStyle = $formatHandler;
         parent::defaultFormat($formatHandler);
         return $this;
     }