| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482 |
- <?php
- /**
- * The excel library of zdoo, can be used to export excel file.
- *
- * @copyright Copyright 2009-2016 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
- * @license ZPL (http://zpl.pub/page/zplv12.html)
- * @author Gang Liu <liugang@cnezsoft.com>
- * @author yaozeyuan <yaozeyuan@cnezsoft.com>
- * @package Excel
- * @version $Id$
- * @link http://www.zdoo.com
- *
- * excel - a library to export excel file, depends on phpexcel.
- *
- * Here are some tips of excelData(named $data) structure maybe uesful. you can use these to create xls/xlsx file . The API is interchangeable.
- *
- * Base property:
- * data->fileName set the fileName
- * data->kind set the kind of excel module
- * data->fields is array like ($field => $fieldTitle). The order of data->fields is also the column's order
- * data->row is array like ($rowNumber => array($field => $value)).This is the data of Excel file. System will fill $value as data into every cell according to $rowNumber and $field
- *
- * Merge cell
- * if there is set data->dataRowspan[$row][$field] or data->dataColspan[$row][$field], the cells will be merge
- * data->dataRowspan[$row][$field] => merge excelColumns[$field]:$rowNumber to excelColumns[$field]:($rowNumber + data->dataRowspan[$row][$field]) into one cell
- * data->dataColspan[$row][$field] => merge excelColumns[$field]:$rowNumber to transIntoExcelKey(int(excelColumns[$field]) + dataColspan[$row][$field]):$rowNumber into one cell
- *
- * html content
- * if you set config->excel->editor[$this->rawExcelData->kind] like 'excelColumns1,excelColumns2...', and excelColumns in that, then $value of all column's cell will be process to remove html tag
- *
- * write sysData and use droplist style on cell
- * sysData is an array like (excelColumns => valueList), system use this sheet to store extraData.
- * if you want to have droplist style on some column in sheet1, you can set data->($exceKey . 'List'), data->listStyle and data->sysData sothat the data will be writen into the sysData sheet and you can see the droplist style is enable.
- * the data->listStyle and data->sysData is an array of series value like ['dropListStyleColumnName' . 'List'] , like ('nameList', 'categoryList', ...) the dropListStyleColumnName used to indicate witch column need that style and data->[dropListStyleColumnName . 'List'] use to transfer data for system get real data to build datalist in sysdata sheet.
- *
- * FreezePane
- * if you set config->excel->freeze->{$this->data->kind}->targetColumnName, this column will be freezed
- *
- * Set width
- * You can set $data->customWidth like array($field => width, $field2 => width, $field3 => width,...) to set width by you like
- * or modify
- * config->excel->titleFields
- * config->excel->centerFields
- * config->excel->dateFields
- * to have default style
- *
- * color
- * if you set data->nocolor, the excel file won't have color
- *
- * File title
- * The lang->excel->title->{data->kind} is the title of data->kind excel file
- *
- * SysData title
- * The lang->excel->title->sysValue is the name of sysData sheet , this is only can use for xls file.
- */
- class excel extends model
- {
- /**
- * __construct
- *
- * @access public
- * @return void
- */
- public function __construct()
- {
- parent::__construct();
- $this->phpExcel = $this->app->loadClass('phpexcel');
- $this->file = $this->loadModel('file');
- $this->sysDataColIndex = 0;
- $this->hasSysData = false;
- }
- /**
- * Init for excel data.
- *
- * @param int $data
- * @access public
- * @return void
- */
- public function init($data)
- {
- $this->rawExcelData = $data;
- $this->fields = $data->fields;
- $this->rows = $data->rows;
- $this->headerRowCount = isset($data->headerRowCount) ? $data->headerRowCount : 1;
- $this->fieldsKey = $this->headerRowCount == 1 ? array_keys($this->fields) : array_keys(reset($this->fields));
- }
- /**
- * Export data to Excel. This is main function.
- *
- * @param object $data
- * @param string $fileType xls | xlsx
- * @param string $savePath, if $savePath != '', then the file will save in $savePath
- * @access public
- * @return void
- */
- public function export($excelData, $fileType = 'xls', $savePath = '', $schemaSheets = '', $schemaSheetIndex = null)
- {
- $sheetIndex = 0;
- foreach($excelData->dataList as $data) $this->phpExcel->createSheet(); // Create sheets.
- foreach($excelData->dataList as $data)
- {
- $this->init($data);
- $this->excelColumns = array();
- foreach($this->fieldsKey as $colIndex => $field) $this->excelColumns[$field] = $this->setExcelField($colIndex);
- /* Set file base property */
- $excelProps = $this->phpExcel->getProperties();
- $excelProps->setCreator('ZenTao');
- $excelProps->setLastModifiedBy('ZenTao');
- $excelProps->setTitle('Office XLS Document');
- $excelProps->setSubject('Office XLS Document');
- $excelProps->setDescription('Document generated by PhpSpreadsheet.');
- $excelProps->setKeywords('office excel PhpSpreadsheet');
- $excelProps->setCategory('Result file');
- $excelSheet = $this->phpExcel->getSheet($sheetIndex);
- $sheetTitle = isset($this->rawExcelData->title) ? $this->rawExcelData->title : $this->rawExcelData->kind;
- if($sheetTitle) $excelSheet->setTitle($sheetTitle);
- $sheetRow = 1;
- if($this->headerRowCount == 1)
- {
- foreach($this->fields as $field => $fieldName)
- {
- $cell = $this->excelColumns[$field] . $sheetRow;
- $excelSheet->setCellValueExplicit($cell, $fieldName, \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_STRING);
- }
- $sheetRow++;
- }
- else
- {
- foreach($this->fields as $fieldIndex => $fields)
- {
- foreach($fields as $field => $fieldName)
- {
- if(isset($this->excelColumns[$field]))
- {
- $cell = $this->excelColumns[$field] . $sheetRow;
- /* Merge Cells.*/
- if(isset($this->rawExcelData->headerRowspan[$fieldIndex][$field]) && is_int($this->rawExcelData->headerRowspan[$fieldIndex][$field]))
- {
- $endCell = $this->excelColumns[$field] . ($sheetRow + $this->rawExcelData->headerRowspan[$fieldIndex][$field] - 1);
- $excelSheet->mergeCells($cell . ":" . $endCell);
- }
- if(isset($this->rawExcelData->headerColspan[$fieldIndex][$field]) && is_int($this->rawExcelData->headerColspan[$fieldIndex][$field]))
- {
- $column = $this->setExcelField($this->rawExcelData->headerColspan[$fieldIndex][$field] - 1, $this->excelColumns[$field]);
- $endCell = $column . $sheetRow;
- $excelSheet->mergeCells($cell . ":" . $endCell);
- }
- $excelSheet->setCellValueExplicit($cell, $fieldName, \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_STRING);
- }
- }
- $sheetRow++;
- }
- }
- /* Write system data in excel.*/
- $this->writeSysData();
- $skipNumberCell = array(); // When merging cells, data cannot be merged. When assigning values to cells, it is necessary to skip merging data other than the first row
- foreach($this->rows as $rowIndex => $row)
- {
- foreach($row as $field => $value)
- {
- if(isset($this->excelColumns[$field]))
- {
- $cell = $this->excelColumns[$field] . $sheetRow;
- /* Merge Cells.*/
- if (isset($this->rawExcelData->dataRowspan[$rowIndex][$field]) && is_int($this->rawExcelData->dataRowspan[$rowIndex][$field]))
- {
- $endCell = $this->excelColumns[$field] . ($sheetRow + $this->rawExcelData->dataRowspan[$rowIndex][$field] - 1);
- /* Merge underlying data. */
- for($row = $sheetRow + 1; $row <= $sheetRow + $this->rawExcelData->dataRowspan[$rowIndex][$field] - 1; $row++)
- {
- $skipNumberCell[] = $this->excelColumns[$field] . $row;
- }
- $excelSheet->mergeCells($cell . ":" . $endCell);
- }
- if(isset($this->rawExcelData->dataColspan[$rowIndex][$field]) && is_int($this->rawExcelData->dataColspan[$rowIndex][$field]))
- {
- $column = $this->setExcelField($this->rawExcelData->dataColspan[$rowIndex][$field] - 1, $this->excelColumns[$field]);
- $endCell = $column . $sheetRow;
- $excelSheet->mergeCells($cell . ":" . $endCell);
- }
- /* Wipe off html tags.*/
- if(isset($this->config->excel->editor[$this->rawExcelData->kind]) and in_array($field, $this->config->excel->editor[$this->rawExcelData->kind])) $value = $this->file->excludeHtml($value);
- if(!in_array($cell, $skipNumberCell))
- {
- if(isset($this->rawExcelData->percentageFields[$rowIndex][$field]))
- {
- $excelSheet->setCellValueExplicit($cell, $value, \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_STRING);
- }
- elseif(isset($this->rawExcelData->numberFields) && in_array($field, $this->rawExcelData->numberFields))
- {
- $excelSheet->setCellValueExplicit($cell, $value, \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_NUMERIC);
- }
- else
- {
- $excelSheet->setCellValueExplicit($cell, $value, \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_STRING);
- }
- }
- /* Add comments to cell, xls don't work, must be xlsx. */
- if(isset($this->rawExcelData->comments[$rowIndex][$field])) $excelSheet->getComment($cell)->getText()->createTextRun($this->rawExcelData->comments[$rowIndex][$field]);
- /* Calculate date. */
- if((strpos($field, 'Date') !== false || in_array($field, $this->config->excel->dateFields)) && !helper::isZeroDate($value)) $excelSheet->setCellValueExplicit($cell, $value, \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_STRING);
- }
- /* Build excel list.*/
- if(isset($this->rawExcelData->listStyle) and in_array($field, $this->rawExcelData->listStyle)) $this->buildList($excelSheet, $field, $sheetRow);
- }
- $sheetRow++;
- }
- $this->setStyle($excelSheet, $sheetRow - 1);
- $this->setAlignment($excelSheet);
- if(isset($this->rawExcelData->help))
- {
- $excelSheet->mergeCells("A" . $sheetRow . ":" . end($this->excelColumns) . $sheetRow);
- $excelSheet->setCellValue("A" . $sheetRow, $this->rawExcelData->help);
- }
- $sheetIndex++;
- }
- /* If hasn't sys data remove the last sheet. */
- if(!$this->hasSysData) $this->phpExcel->removeSheetByIndex($this->phpExcel->getSheetCount() - 1);
- $this->phpExcel->setActiveSheetIndex(0);
- if(!empty($schemaSheets))
- {
- foreach($schemaSheets as $schemaSheet)
- {
- $schemaSheetRows = $schemaSheet->getHighestRow();
- if($schemaSheetRows > 1) $this->phpExcel->addSheet($schemaSheet, $schemaSheetIndex);
- }
- }
- /* Encode the file name for IE. */
- $fileName = $excelData->fileName;
- if(strpos($this->server->http_user_agent, 'MSIE') !== false || strpos($this->server->http_user_agent, 'Trident') !== false) $fileName = urlencode($fileName);
- /* Clean the ob content to make sure no space or utf-8 bom output. */
- $obLevel = ob_get_level();
- for($i = 0; $i < $obLevel; $i++) ob_end_clean();
- /* Set the file name and save path. */
- if(preg_match('/Safari/', $_SERVER['HTTP_USER_AGENT']))
- {
- $fileName = rawurlencode($fileName);
- $attachment = "attachment; filename*=utf-8''{$fileName}.{$fileType}";
- }
- else
- {
- $fileName = str_replace('+', '%20', urlencode($fileName));
- $attachment = "attachment; filename=\"{$fileName}.{$fileType}\";";
- }
- /* Set the download headers. */
- helper::setcookie('downloading', 1, 0, $this->config->webRoot, '', false, false);
- helper::header('Content-Type', 'application/vnd.ms-excel');
- helper::header('Content-Disposition', $attachment);
- helper::header('Cache-Control', 'max-age=0');
- $excelWriter = $this->phpExcel->createWriter($fileType);
- $excelWriter->setPreCalculateFormulas(false);
- $excelWriter->save($savePath ?: 'php://output');
- exit;
- }
- /**
- * Set excel filed name.
- *
- * @param int $colIndex
- * @param string $column
- * @access public
- * @return string
- */
- public function setExcelField($colIndex, $column = 'A')
- {
- for($col = 1; $col <= $colIndex; $col++) $column++;
- return $column;
- }
- /**
- * Write SysData sheet in xls.
- *
- * @access public
- * @return void
- */
- public function writeSysData()
- {
- if(!isset($this->rawExcelData->sysDataList)) return true;
- $this->hasSysData = true;
- $sheetIndex = $this->phpExcel->getSheetCount() - 1;
- $this->phpExcel->getSheet($sheetIndex)->setTitle($this->lang->excel->title->sysValue);
- foreach($this->rawExcelData->sysDataList as $field)
- {
- $column = $this->setExcelField($this->sysDataColIndex);
- $dataKey = $field . 'List';
- if(!isset($this->rawExcelData->$dataKey)) continue;
- $row = 1;
- foreach($this->rawExcelData->$dataKey as $value)
- {
- $this->phpExcel->getSheet($sheetIndex)->setCellValueExplicit("$column$row", $value, \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_STRING);
- $row++;
- }
- $this->sysDataColIndex++;
- }
- }
- /**
- * Build dropmenu list.
- * For a tip , if you want to modify that function , search "phpExcel DataValidation namedRange" in stackoverflow.com maybe helpful.
- *
- * @param int $excelSheet
- * @param int $field
- * @param int $row
- * @access public
- * @return void
- */
- public function buildList($excelSheet, $field, $row)
- {
- $listName = $field . 'List';
- $colIndex = array_search($field, $this->rawExcelData->sysDataList);
- $column = $this->setExcelField($colIndex);
- if(isset($this->rawExcelData->$listName))
- {
- $itemCount = count($this->rawExcelData->$listName);
- if($itemCount == 0) $itemCount = 1;
- $range = "{$this->lang->excel->title->sysValue}!\${$column}\$1:\${$column}\$" . $itemCount;
- }
- else
- {
- $range = is_array($this->rawExcelData->$listName) ? '' : '"' . $this->rawExcelData->$listName . '"';
- }
- $objValidation = $excelSheet->getCell($this->excelColumns[$field] . $row)->getDataValidation();
- $objValidation->setType(\PhpOffice\PhpSpreadsheet\Cell\DataValidation::TYPE_LIST)
- ->setErrorStyle(\PhpOffice\PhpSpreadsheet\Cell\DataValidation::STYLE_INFORMATION)
- ->setAllowBlank(false)
- ->setShowErrorMessage(false)
- ->setShowDropDown(true)
- ->setErrorTitle($this->lang->excel->error->title)
- ->setError($this->lang->excel->error->info)
- ->setFormula1($range);
- }
- /**
- * Set excel style.
- *
- * @param object $excelSheet
- * @param int $endRow
- * @access public
- * @return void
- */
- public function setStyle($excelSheet, $endRow)
- {
- $startRow = $this->headerRowCount + 1;
- $endColumn = $this->setExcelField(count($this->excelColumns) - 1);
- if(isset($this->rawExcelData->help) and isset($this->rawExcelData->extraNum)) $endRow--;
- /* Freeze column.*/
- if(isset($this->config->excel->freeze->{$this->rawExcelData->kind}))
- {
- $column = $this->excelColumns[$this->config->excel->freeze->{$this->rawExcelData->kind}];
- $column++;
- $excelSheet->FreezePane($column . $startRow);
- }
- /* Set row height. */
- $excelSheet->getDefaultRowDimension()->setRowHeight(20);
- /* Set content style for this table.*/
- $contentStyle = $excelSheet->getStyle("A1:{$endColumn}{$endRow}");
- $contentStyle->getAlignment()
- ->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_LEFT)
- ->setVertical(\PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER)
- ->setWrapText(true);
- $contentStyle->getBorders()->getAllBorders()
- ->setBorderStyle(\PhpOffice\PhpSpreadsheet\Style\Border::BORDER_THIN)
- ->getColor()->setARGB('FF808080');
- $contentStyle->getFont()->setSize(9);
- if(!isset($this->rawExcelData->nocolor))
- {
- $contentStyle->getFill()
- ->setFillType(\PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID)
- ->getStartColor()->setARGB('FFB2D7EA');
- }
- /* Set header style for this table.*/
- $headerStyle = $excelSheet->getStyle("A1:{$endColumn}{$this->headerRowCount}");
- $headerStyle->getAlignment()->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER);
- $headerStyle->getFont()->setBold(true);
- if(!isset($this->rawExcelData->nocolor))
- {
- $headerStyle->getFill()->getStartColor()->setARGB('FF343399');
- $headerStyle->getFont()->getColor()->setARGB('FFFFFFFF');
- }
- /* Set column width and cell style. */
- $customWidth = $this->rawExcelData->customWidth ?? [];
- foreach($this->excelColumns as $key => $column)
- {
- /* Set column width. */
- $columnWidth = 10;
- if(strpos($key, 'Date') !== false || in_array($key, $this->config->excel->dateFields)) $columnWidth = 12; // Set column width of date fields.
- if(in_array($key, $this->config->excel->titleFields)) $columnWidth = $this->config->excel->width->title ?? 25; // Set column width of title fields.
- if(isset($this->config->excel->editor[$this->rawExcelData->kind]) and in_array($key, $this->config->excel->editor[$this->rawExcelData->kind])) $columnWidth = $this->config->excel->width->content ?? 50; // Set column width of editor fields.
- if(isset($customWidth[$key])) $columnWidth = $customWidth[$key]; // Set column width by userdefined settings.
- $excelSheet->getColumnDimension($column)->setWidth($columnWidth);
- /* Set cell alignment of center fields. */
- if(in_array($key, $this->config->excel->centerFields))
- {
- $excelSheet->getStyle("{$column}{$startRow}:{$column}{$endRow}")->getAlignment()->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER);
- }
- /* Set cell format of date fields. */
- if(strpos($key, 'Date') !== false || in_array($key, $this->config->excel->dateFields))
- {
- $excelSheet->getStyle("{$column}{$startRow}:{$column}{$endRow}")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_DATE_YYYYMMDD2);
- }
- }
- /* Set row color. */
- if(!isset($this->rawExcelData->nocolor))
- {
- if(isset($this->rawExcelData->colors))
- {
- foreach($this->rawExcelData->colors as $row => $color)
- {
- $beginColumn = $this->excelColumns[$color->begin];
- $endColumn = $this->excelColumns[$color->end];
- $excelSheet->getStyle("{$beginColumn}{$row}:{$endColumn}{$row}")->getFill()->getStartColor()->setRGB($color->color);
- }
- }
- else
- {
- if($startRow % 2 == 0) $startRow++; // If data start row is even, plus 1 to make it odd.
- /* Set odd row background color. */
- for($row = $startRow; $row <= $endRow; $row += 2)
- {
- $excelSheet->getStyle("A{$row}:{$endColumn}{$row}")->getFill()->getStartColor()->setARGB('FFDEE6FB');
- }
- }
- }
- }
- /**
- * Set alignment.
- *
- * @param object $excelSheet
- * @access public
- * @return bool
- */
- public function setAlignment($excelSheet)
- {
- if(!isset($this->rawExcelData->percentageFields)) return true;
- foreach($this->rawExcelData->percentageFields as $row => $fields)
- {
- foreach($fields as $key => $field)
- {
- if(isset($this->excelColumns[$key]))
- {
- $cell = $this->excelColumns[$key] . ($this->headerRowCount + $row + 1);
- $excelSheet->getStyle($cell)->getAlignment()->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_RIGHT);
- }
- }
- }
- }
- }
|