PHPWord 表格克隆多行
发表于:2023-04-20 10:55:03浏览:674次
直接上代码
<?php
namespace app\shop\service;
use PhpOffice\PhpWord\TemplateProcessor;
/**
* 自定义 克隆多行
* Class PhpWordService
* @package Home\Service
*/
class PhpWordService extends TemplateProcessor
{
/**
* 查找字符、字符串第n次出现的位置
* todo 自行添加
* @param string $str 原始字符串
* @param string $find 需要查找的字符、字符串
* @param int $start 开始搜索的位置
* @param int $num 第几次出现的字符、字符串
* @return bool|int 返回第n次出现的位置
*/
public function strPosNum($str = '', $find = '', $start = 0, $num = 1)
{
$pos = false;
if ($num > 0) {
$pos = strpos($str, $find, $start);
for ($i = 1; $i < $num; $i++) {
$pos = strpos($str, $find, $pos + 1);
}
}
return $pos;
}
/**
* 找到下面几行的最后行尾的结束位置
* todo 自行添加
* @param $offset 开始搜索的位置
* @param int $rowNum 复制几行
* @return bool|int
*/
public function findRowEndWithRowNum($offset, $rowNum = 0)
{
// </w:tr> 行尾标签
return $this->strPosNum($this->tempDocumentMainPart, '</w:tr>', $offset, $rowNum) + 7;
}
/**
* 克隆多行
* todo 自行添加
* 如果clone的第一行是合并单元格的 就clone变量选择第一个合并单元格的 如下
* ---------------------
* | | B |
* | A |--------|
* | | C | D |
* ---------------------
* @param $search 搜索 这个要选择克隆多行的第一行的变量
* @param $numberOfClones 克隆的个数
* @param int $rowNum 克隆这行下面几行(包括当前行)
*/
public function cloneMultiRow($search, $numberOfClones = 0, $rowNum = 1)
{
// 搜索的字符串替换成word中使用的变量
$search = static::ensureMacroCompleted($search);
// 查找 当前变量 字符串首次出现的位置
$tagPos = strpos($this->tempDocumentMainPart, $search);
if (!$tagPos) {
throw new \PhpOffice\PhpWord\Exception\Exception('Can not clone row, template variable not found or variable contains markup.');
}
// 找到行的开始位置
$rowStart = $this->findRowStart($tagPos);
// 需要行尾的结束位置
$rowEnd = $this->findRowEndWithRowNum($tagPos, $rowNum);
// 找到 行开始和行结束中间的 xml字符串
$xmlRow = $this->getSlice($rowStart, $rowEnd);
// 碰到合并单元格的
// Check if there's a cell spanning multiple rows.
if (preg_match('#<w:vMerge w:val="restart"/>#', $xmlRow)) {
// $extraRowStart = $rowEnd;
$extraRowEnd = $rowEnd;
while (true) {
$extraRowStart = $this->findRowStart($extraRowEnd + 1);
$extraRowEnd = $this->findRowEndWithRowNum($extraRowEnd + 1, $rowNum);
// If extraRowEnd is lower then 7, there was no next row found.
if ($extraRowEnd < 7) {
break;
}
// If tmpXmlRow doesn't contain continue, this row is no longer part of the spanned row.
$tmpXmlRow = $this->getSlice($extraRowStart, $extraRowEnd);
if (!preg_match('#<w:vMerge/>#', $tmpXmlRow) &&
!preg_match('#<w:vMerge w:val="continue"\s*/>#', $tmpXmlRow)) {
break;
}
// This row was a spanned row, update $rowEnd and search for the next row.
$rowEnd = $extraRowEnd;
}
$xmlRow = $this->getSlice($rowStart, $rowEnd);
}
// 获取 tempDocumentMainPart 中 开头到 查找行开始位置的字符串
$result = $this->getSlice(0, $rowStart);
// 获取 clone变量 之后的xml 并拼接
$result .= implode($this->indexClonedVariables($numberOfClones, $xmlRow));
// 获取 行结尾到 xml的结束的字符串 并拼接
$result .= $this->getSlice($rowEnd);
$this->tempDocumentMainPart = $result;
}
}
调用(不能直接用,参考一下逻辑就行):
<?php
namespace app\shop\service;
use think\admin\Service;
use PhpOffice\PhpWord\TemplateProcessor;
/**
* Word文档服务
* Class OfficeWordService
* @package app\shop\service
*/
class OfficeWordService extends Service
{
/**
* 生成订单文档
*
* @param string $orderNo 订单号
* @return array
*/
public static function mkOrderWord(string $orderNo): array
{
// 查询订单数据
$order = [];
// 查询商品数据
$goods = [];
// 查询附件数据
$attachments = [];
try {
$template = new PhpWordService($shop_order_base['order_template']);
// 设置自己的字段数据
$template->setValues([]);
// 商品
if ($goods) {
$template->cloneMultiRow('goodscover', count($goods), 3);
foreach ($goods as $key => $value) {
$value['goods_cover'] = syspath('public'.parse_url($value['goods_cover'], PHP_URL_PATH));
if (is_file($value['goods_cover']) && getimagesize($value['goods_cover'])) {
$template->setImageValue('goodscover#'.($key+1), ['path'=>$value['goods_cover'],'width'=>100,'height'=>100]);
} else {
$template->setValue('goodscover#'.($key+1), '');
}
$template->setValue("goodsname#".($key+1), $value['goods_name']);
$template->setValue("goodsspec#".($key+1), $value['goods_spec']);
$template->setValue("goodsprice#".($key+1), $value['price_selling']);
$template->setValue("goodstotal#".($key+1), $value['total_selling']);
$template->setValue("goodsnum#".($key+1), $value['stock_sales']);
}
}
// 附件
if ($attachments) {
$template->cloneMultiRow('attrdesc', count($attachments), 1);
foreach ($attachments as $k => $v) {
$template->setValue("attrdesc#".($k+1), $v['desc']??'暂无描述');
$v['images_arr'] = empty($v['images_arr']) ? [] : $v['images_arr'];
for ($i=0; $i < OrderService::ATTR_MAX_IMAGE; $i++) {
$img = syspath('public'.parse_url($v['images_arr'][$i] ?? '', PHP_URL_PATH));
$f = 'attrimg'.($i+1).'#';
if (is_file($img) && getimagesize($img)) {
$template->setImageValue($f.($k+1), ['path'=>$img,'width'=>100,'height'=>100]);
} else {
$template->setValue($f.($k+1), '');
}
}
}
} else {
$template->setValue('attrdesc', '');
for ($i=0; $i < OrderService::ATTR_MAX_IMAGE; $i++) {
$template->setValue('attrimg'.($i+1), '');
}
}
$template->saveAs($syspath.'/'.$save_name);
} catch (\Exception $e) {
return [0, '生成word失败,'.$e->getMessage()];
}
$info = LocalStorage::instance()->info('word/order/'.$save_name);
if (empty($info)) return [0, '获取文件地址失败'];
return [1, $info];
}
}