grid; $columns = $this->getColumns(); $columns = $this->addingColumn($columns); $call_back = function ($page, $limit) use ($model, $grid, $columns) { $model->page = $page; $grid->paginate($limit); $model->setGrid($grid); request()->offsetSet('per_page', $limit); $list = $model->getQuery()->get()->toArray(); $list = $this->dealColumn($columns, $list); if ($this->columnCallback) { $list = call_user_func($this->columnCallback, $columns, $list); } return $list; }; if ($this->debug) { dd($columns, call_user_func($call_back, 1, 20)); } $file_name = $this->fileName ? $this->fileName : $this->getTable().date('YmdHis'); $data_count = $this->getQuery()->count(); $perPage = request()->get('per_page', $this->perLimit); $this->exportCsv($file_name, $columns, $call_back, $data_count, $perPage); } /** * 设置文件名 * * @param null $file_name */ public function setName($file_name = null) { if ($file_name) { $this->fileName = $file_name; } } /** * Notes: 设置处理字段值的规则 * * @Author: 玄尘 * @Date: 2023/2/1 15:42 * @param $column * @param \Closure $call_back */ public function setColumn($column, \Closure $call_back) { $this->dealColumnList[$column] = $call_back; } /** * 添加字段 * * @param $column * @param $name * @param null|string|\Closure $after_column * @param null|\Closure $call_back */ public function addColumn($column, $name, $after_column = null, $call_back = null) { if ($after_column instanceof \Closure) { $this->addColumnList[$column] = $after_column; $this->appendColumn[$column] = [$name, null]; } else { if ($call_back instanceof \Closure) { $this->addColumnList[$column] = $call_back; $this->appendColumn[$column] = [$name, $after_column]; } else { $this->appendColumn[$column] = [$name, $after_column]; } } } /** * 正式追加字段 * * @param $columns * @return array */ protected function addingColumn($columns): array { if (! $this->appendColumn) { return $columns; } $keys = array_keys($columns); foreach ($this->appendColumn as $column => $item) { list ($name, $after_column) = $item; if (! empty($after_column) && in_array($after_column, $keys)) { $index = array_search($after_column, $keys); array_splice($keys, $index + 1, 0, $column); } else { array_push($keys, $column); } $columns[$column] = $name; } $result = []; foreach ($keys as $column) { $result[$column] = $columns[$column] ?? $column; } return $result; } /** * 字段值重新处理 回调函数 * * @param \Closure $call_back */ public function setList(\Closure $call_back) { $this->columnCallback = $call_back; } /** * 处理有共同特征的字段值 * * @param $columns * @param $list * @return mixed */ protected function dealColumn($columns, $list) { if (! $list) { return $list; } $deal = array_keys($this->dealColumnList); $add = array_keys($this->addColumnList); foreach ($list as $index => $data) { foreach ($columns as $column => $name) { switch ($column) { case strpos($column, '.') !== false : $keys = explode('.', $column); $value = $data; for ($i = 0; $i < count($keys); $i++) { $value = $value[$keys[$i]] ?? []; } if (is_array($value)) { $value = empty($value) ? '' : json_encode($value, JSON_UNESCAPED_UNICODE); } $data[$column] = $value; break; } if ($this->dealColumnList && in_array($column, $deal)) { $data[$column] = call_user_func_array($this->dealColumnList[$column], [$data[$column] ?? '', $data]); } if ($this->addColumnList && in_array($column, $add)) { $data[$column] = call_user_func($this->addColumnList[$column], $data); } } $list[$index] = $data; } return $list; } /** * 获取选中的导出字段 * * @return array */ protected function getColumns(): array { $columns = []; foreach ($this->grid->getColumns() as $column) { $columns[$column->getName()] = $column->getLabel(); } $column_string = request()->get('_columns_', ''); if (! $column_string) { return $columns; } $column_array = explode(',', $column_string); $column_list = []; foreach ($columns as $column => $name) { if (in_array($column, $column_array)) { $column_list[$column] = $name; } } return $column_list; } /** * 打印 */ public function dd() { $this->debug = true; } /** * 导出CSV * * @param string $file_name 文件名 * @param array $head_list 表头 * @param int $data_count 总数 * @param int $page_limit 每页数量 * @param \Closure $call_back 获取分页数据的回调函数 */ function exportCsv( string $file_name, array $head_list, \Closure $call_back, int $data_count, int $page_limit = 0 ) { set_time_limit(0); ini_set('memory_limit', -1); header('Content-Type: application/vnd.ms-excel'); header('Content-Disposition: attachment;filename="'.$file_name.'.csv"'); header('Cache-Control: max-age=0'); //打开PHP文件句柄,php://output 表示直接输出到浏览器 $fp = fopen('php://output', 'a'); $head = []; //输出Excel列名信息 foreach ($head_list as $name) { //CSV的Excel支持GBK编码,一定要转换,否则乱码 $head[] = iconv('utf-8', 'gbk', $name); } //将数据通过fputcsv写到文件句柄 fputcsv($fp, $head); $page_limit = $data_count > $page_limit ? $page_limit : $data_count; $page_count = ceil($data_count / $page_limit); for ($page = 1; $page <= $page_count; $page++) { //逐页取出数据,不浪费内存 $data_list = call_user_func($call_back, $page, $page_limit); if (! $data_list) { continue; } foreach ($data_list as $data) { $row = []; foreach ($head_list as $key => $name) { $value = $data[$key] ?? ''; $row[] = iconv('utf-8', 'gbk', $value); } fputcsv($fp, $row); unset($row); } //刷新一下输出buffer,防止由于数据过多内存不足 ob_flush(); flush(); info($page.'/'.$page_count.' time= '.$this->getElapsedTime().' memory= '.$this->getMemoryUsage()); } fclose($fp); exit(); } public function getElapsedTime(int $decimals = 2): string { return number_format(microtime(true) - request()->server('REQUEST_TIME_FLOAT'), $decimals).' s'; } public function getMemoryUsage($precision = 2): string { $size = memory_get_usage(true); $unit = ['b', 'kb', 'mb', 'gb', 'tb', 'pb']; return round($size / pow(1024, ($i = floor(log($size, 1024)))), $precision).' '.$unit[$i]; } }