数据导出常规方式(经验分享贴)导出
一、说明
-
使用常规的导出方式,不走计划任务,数据量大的小伙伴慎用。
-
依赖
phpoffice/phpspreadsheet
库。 -
优点:可以直接使用BuildAdmin原有的数据筛选功能,列表所查即导出,避免重复造轮子,方便数据处理,自定义筛选等操作。
-
缺点:比较占内存,大数据量的建议限制导出数量。
-
直接上图
二、前端代码
在页面中添加导出按钮
找到页面vue代码中<TableHeader>
组件,使用自定义插槽添加按钮。
<!-- 表格顶部菜单 -->
<!-- 自定义按钮请使用插槽,甚至公共搜索也可以使用具名插槽渲染,参见文档 -->
<TableHeader
:buttons="['refresh', 'add', 'edit', 'delete', 'comSearch', 'quickSearch', 'columnDisplay']"
:quick-search-placeholder="t('Quick search placeholder', { fields: t('article.quick Search Fields') })"
>
<!-- 自定义按钮 -->
<template #default>
<el-button color="#40485b" class="table-header-operate" type="info" style="margin-left: 12px;" @click="exportHandle">
<Icon color="#ffffff" name="el-icon-RefreshRight" />
<span class="table-header-operate-text">导出</span>
</el-button>
</template>
</TableHeader>
在script
中定义点击事件,放在const baTable = new baTableClass
后面
//这段可以移到上面去
import { ElNotification } from 'element-plus'
import { ElLoading } from 'element-plus'
const exportHandle = ()=>{
let params = {}
//带上页面筛选数据
Object.assign(params, baTable.table.filter)
//加载中转圈圈
const loadIndex = ElLoading.service()
//请求url,请求的是当前业务控制器的export方法
baTable.api.postData('export', params).then(res=>{
//关闭加载中
loadIndex.close()
if(res.code == 1 && res.data.full_url){
//下载excel文件
window.open(res.data.full_url)
}else{
ElNotification({
message: '请求错误',
type: 'error',
})
}
})
}
三、后端代码
引入依赖
composer require phpoffice/phpspreadsheet
在业务控制器中添加导出方法
public function export()
{
try {
list($where, $alias, $limit, $order) = $this->queryBuilder();
$res = $this->model
->field($this->indexField)
->withJoin($this->withJoinTable, $this->withJoinType)
->alias($alias)
->where($where)
->order($order)
->select()
->toArray();
//以上部分直接复制的index控制器,然后将paginate()方法改成select()->toArray()
//创建导出类实例
$export = new \alei\Export('文章');
//设置行参数
$export->setColumn([
['field' => 'id', 'title' => '编号'],
['field' => 'title', 'title' => '标题'],
['field' => 'create_time', 'title' => '创建时间', 'formater' => 'time'], //格式化时间,时间戳格式化为时间日期格式,默认格式: “Y-m-d H:i:s”
['field' => 'update_time', 'title' => '更新时间', 'formater' => 'time', 'format_time'=>'Y/m/d H:i'], // format_time自定义时间格式
['field' => 'weigh', 'title' => '排序'],
['field' => 'content', 'title' => '详情', 'formater' => function ($value, $row, $index) { //自定义过滤函数
return strip_tags($value);
}]
]);
//设置数据
$export->setData($res);
//生成表格,返回url
$url = $export->build(); //'export/文章.xlsx'
//完整域名
$full_url = 'http://localhost:8000' . $url;
} catch (\Throwable $th) {
$this->error($th->getMessage());
}
$this->success('', compact('url', 'full_url'));
}
引入封装的导出类
在根目录extend
目录中,新建目录alei
,目录名可自定义,如果自定义的话记得修改类中的命名空间。在新建的目录中,新建Export.php
,将下面的代码粘贴进去。
<?php
namespace alei;
use PhpOffice\PhpSpreadsheet\Cell\DataType;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Style\Alignment;
use PhpOffice\PhpSpreadsheet\Style\Border;
use PhpOffice\PhpSpreadsheet\Style\Fill;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
/**
* 导出类
*/
class Export
{
//导出选项
private $option = [
//导出文件路径
'file_path' => '',
//导出文件名
'file_name' => '导出文件',
//首行是否加粗
'header_bold' => true,
//垂直居中
'header_vertical_center' => true,
//水平居中
'header_horizontal_center' => true,
//首列高度
'header_row_height' => 30
];
private $column;
private $data;
/**
* @params Array|String $option_or_file_name 导出选项或者文件名
* @params String $file_path 文件保存路径
*/
public function __construct($option_or_file_name = null, $file_path = null)
{
if(!is_null($option_or_file_name)){
if(is_array($option_or_file_name)) {
//设置导出选项
$this->option = array_merge($this->option, $option_or_file_name);
}else if(is_string($option_or_file_name) && strlen($option_or_file_name) > 0){
//设置保存的文件名
$this->option['file_name'] = $option_or_file_name;
}
}
//设置保存的文件路径
if (!is_null($file_path) && is_string($file_path) && strlen($file_path) > 0) {
$this->option['file_path'] = $file_path;
}
}
/**
* 设置列参数
* @param String field 字段名
* @param String title 标题
* @param Int width 列宽
* @param Function|String formater [格式化器,参数1:字段值,参数2:行数据,参数3:行号] 或 [预定义方法:'time':格式化时间]
* @param String type 类型 default 'text'
* @param Boole vertical_center 是否垂直居中,default false
* @param Boole horizontal_center 是否水平居中,default false
*/
public function setColumn($column)
{
$this->column = $column;
}
/**
* 格式化行参数
*/
private function formatRowData($data)
{
$tmp = [];
foreach ($this->column as $key => $column) {
if (isset($data[$column['field']])) {
$tmp[$column['field']] = $data[$column['field']];
} else {
$tmp[$column['field']] = '';
}
if (isset($column['formater'])) {
if (gettype($column['formater']) === 'object' && is_callable($column['formater'])) {
$tmp[$column['field']] = $column['formater']($tmp[$column['field']], $data, $key);
} else if (is_string($column['formater'])) {
//格式化
switch($column['formater']){
case 'time': //时间戳转时间
$format_time = $column['format_time'] ?? 'Y-m-d H:i:s';
if (empty($tmp[$column['field']])) {
$tmp[$column['field']] = '';
} else {
$tmp[$column['field']] = date($format_time, $tmp[$column['field']]);
}
break;
default :
}
}
}
}
return $tmp;
}
/**
* 设置数据
*/
public function setData($list)
{
if (empty($this->column)) throw new \think\Exception('Please set the column parameters first!');
$data = [];
foreach ($list as $key => $val) {
$data[] = $this->formatRowData($val);
unset($list[$key]);
}
$this->data = $data;
}
/**
* 渲染表格并返回
* @params $column 列参数
* @params $data 导出数据
*/
public function build()
{
if (empty($this->column)) {
throw new \think\Exception('Please set the column parameters first!');
}
$spreadsheet = new Spreadsheet();
// 1获取活动工作薄
$sheet = $spreadsheet->getActiveSheet();
//最后一列列号
$end_row = count($this->column);
// 首行选择器
$first_cell = [1, 1, $end_row, 1];
//设置背景色
$sheet->getStyle($first_cell)->getFill()->setFillType(Fill::FILL_SOLID);
$sheet->getStyle($first_cell)->getFill()->getStartColor()->setARGB('FFeeeeee');
//首行加边框
$sheet->getStyle($first_cell)->getBorders()->applyFromArray([
'allBorders' => [
'borderStyle' => Border::BORDER_HAIR,
'color' => ['argb' => '666666']
]
]);
//加粗
if ($this->option['header_bold']) {
$sheet->getStyle($first_cell)->getFont()->setBold(true);
}
//垂直居中
if ($this->option['header_vertical_center']) {
$sheet->getStyle($first_cell)->getAlignment()->setVertical(Alignment::VERTICAL_CENTER);
}
//水平居中
if ($this->option['header_horizontal_center']) {
$sheet->getStyle($first_cell)->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER);
}
//首行高度
$sheet->getRowDimension(1)->setRowHeight($this->option['header_row_height']);
foreach ($this->column as $key => $column) {
$sheet->setCellValue([$key + 1, 1], $column['title']);
$sheet->getColumnDimensionByColumn($key + 1)->setAutoSize(true);
}
foreach ($this->data as $key => $row) {
foreach ($this->column as $k => $column) {
$value = $row[$column['field']];
$cell = [$k + 1, $key + 2];
//换行
if (mb_strpos($value, PHP_EOL) !== false) {
$sheet->getStyle($cell)->getAlignment()->setWrapText(true);
}
//垂直居中
if (!empty($column['vertical_center'])) {
$sheet->getStyle($cell)->getAlignment()->setVertical(Alignment::VERTICAL_CENTER);
}
//水平居中
if (!empty($column['horizontal_center'])) {
$sheet->getStyle($cell)->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER);
}
//设置文字
$sheet->setCellValueExplicit($cell, $value, DataType::TYPE_STRING);
}
unset($this->data[$key]);
}
//设置工作表标题
$sheet->setTitle($this->option['file_name']);
$this->option['file_path'] = $this->option['file_path'] == '' ? '' : ($this->option['file_path'] . '/' );
$url = '/export/' . $this->option['file_path'] . $this->option['file_name'] . '.xlsx';
$filePath = public_path() . str_replace('/', DIRECTORY_SEPARATOR, $url);
if( is_file( $filePath ) ){
//如果文件已经导出过则删除旧文件
unlink( $filePath );
}else if (!is_dir(dirname($filePath))) {
mkdir(dirname($filePath), 0700, true);
}
// 保存电子表格
$writer = new Xlsx($spreadsheet);
$writer->save($filePath);
return $url;
}
}
设置行参数$export->setColumn()
参数说明
//设置行参数
$export->setColumn([
['field' => 'id', 'title' => '编号'],
['field' => 'title', 'title' => '标题'],
['field' => 'create_time', 'title' => '创建时间', 'formater' => 'time'], //格式化时间,时间戳格式化为时间日期格式,默认格式: “Y-m-d H:i:s”
['field' => 'update_time', 'title' => '更新时间', 'formater' => 'time', 'format_time'=>'Y/m/d H:i'], // format_time自定义时间格式
['field' => 'weigh', 'title' => '排序'],
['field' => 'content', 'title' => '详情', 'formater' => function ($value, $row, $index) { //自定义过滤函数
return strip_tags($value);
}]
]);
field
string 字段名,对应数据库字段名。title
string 标题,作为导出的excel表头。formater
string | function 格式化参数- string类型: 传
time
,可将时间戳格式化为日期,默认格式Y-m-d H:i:s
,可配合format_time
参数自定义格式。 - function类型:自定义格式化函数。返回参数1: $value, 参数2: $row, 参数3: $value。
- string类型: 传
format_time
string 若formater
字段参数为time
, 则此字段可以自定义时间格式。vertical_center
bool 是否垂直居中。horizontal_center
bool 是否水平居中。
请先登录
感谢分享~
感谢分享
感谢😋
每个表的前后端都得改么?
使用场景比较多的话,可以封装一下,后端代码放到基础控制器,前端代码封装成组件
感谢分享
- 1
前往