V2.0 - 插件 在线选题生成试卷(A4) / 通过模板文件生成word
yoyocmf 2020-04-16 13:23:34

下载插件包,放到 /plugins 目录下,支持扩展 16K 等,只要上传模板即可

 

安装扩展管理

https://github.com/PHPOffice/PHPWord

PHP Code复制内容到剪贴板
  1. composer require phpoffice/phpword:dev-master  

 

1、根据现有的word模板,生成word,仅限文字版,不含图片

例:生成审计通知书

PHP Code复制内容到剪贴板
  1. <?php  
  2. namespace plugins\GenerateWord\components;  
  3.   
  4. use backend\models\Project;  
  5. use backend\models\ProjectInstruments;  
  6. use common\modules\user\models\Department;  
  7. use common\modules\user\models\User;  
  8. use PhpOffice\PhpWord\TemplateProcessor;  
  9. use yii\base\Model;  
  10.   
  11. class GenerateDocByProject  
  12. {  
  13.     /** 
  14.      * @var string 试卷模板 
  15.      */  
  16.     public $template;  
  17.   
  18.     private $model;  
  19.   
  20.     public function __construct(Model $model$type = 1)  
  21.     {  
  22.         $this->model = $model;  
  23.         // 不支持doc  
  24.         $this->template = dirname(__DIR__) . '/resources/project_template_' . $type . '.docx';  
  25.     }  
  26.   
  27.     /** 
  28.      * 下载审计通知书 
  29.      * @return string 
  30.      * @throws \PhpOffice\PhpWord\Exception\CopyFileException 
  31.      * @throws \PhpOffice\PhpWord\Exception\CreateTemporaryFileException 
  32.      * @throws \PhpOffice\PhpWord\Exception\Exception 
  33.      */  
  34.     public function downloadNotice(){  
  35.         $company = \Yii::$app->config->get("sitename");  // 最上面第一行标题  
  36.         $code = $this->model->code; // 第三行,系统编号  
  37.         $audit = "我司审计处";    // 审计处名称,内容第一个变量  
  38.         $audited_object = " ";   // 被审计对象,内容第二个变量  
  39.   
  40.         $projectModel = Project::findOne(["id"=>$this->model->project_id]);  
  41.         $audit_users = $projectModel->getMembersToList();// 审计组成员  
  42.         $description = $this->model->description;       // 中间内容  
  43.   
  44.         $title = $projectModel->title;  // 项目名称,内容第三个变量  
  45.   
  46.         if($projectModel->audit_type == 1 && $projectModel->audit_users){  
  47.             // 如果是对人员审计并且有选择人员的话  
  48.             $useIds = explode(",",$projectModel->audit_users);  
  49.             $users = User::find()->where(["in","id",$useIds])->asArray()->all();  
  50.             $users = array_map(function ($user) {  
  51.                 return $user["true_name"]?$user["true_name"]:$user["username"];  
  52.             }, $users);  
  53.             $audited_object = implode("、",$users);  
  54.   
  55.         }else if($projectModel->audit_type == 0 && $projectModel->audit_department_id){  
  56.             // 0是部门  
  57.             $audited_object = Department::find()->select("title")->where(["id"=>$projectModel->audit_department_id])->scalar();  
  58.         }  
  59.   
  60.         if(!$description){  
  61.             // 默认内容  
  62.             $startTime = date("Y年m月d日",strtotime($projectModel->start_time));  
  63.             $description = "自".$startTime."起,对".$audited_object."进行审计(".$title."),必要时将追溯到相关年度或者延伸审计(调查)。请予以配合,并提供有关资料(包括电子数据资料)和必要的工作条件。";// 通知书主体默认内容  
  64.         }  
  65.   
  66.         $audit_users_text = [];  
  67.         foreach ($audit_users as $k => $v){  
  68.             $item = $v["name"] . ":";  
  69.             foreach ($v["users"as $kk => $u){  
  70.                 $item .= $u["true_name"]?$u["true_name"]:$u["username"];  
  71.                 $kk == 0 ? ($item.=""):($item.=' ');  
  72.             }  
  73.             $audit_users_text[]["line"] = $item;  
  74.         }  
  75.   
  76.         $templateProcessor = new TemplateProcessor($this->template);  
  77.         $templateProcessor->setValue('company'$company);  
  78.         $templateProcessor->setValue('code'$code);  
  79.         $templateProcessor->setValue('audit'$audit);  
  80.         $templateProcessor->setValue('audited_object'$audited_object);  
  81.         $templateProcessor->setValue('title'$title);  
  82.         $templateProcessor->setValue('description'$description);  
  83.         $templateProcessor->setValue('today'date("Y年m月d日"));  
  84.         $templateProcessor->cloneBlock('audit_users', 0, true, false, $audit_users_text);  
  85.         return $templateProcessor->save();  
  86.     }  
  87.   
  88.     /** 
  89.      * 下载审计调查了解函 
  90.      */  
  91.     public function downloadInvestigationLetter(){  
  92.         $company = \Yii::$app->config->get("sitename");  // 最上面第一行标题  
  93.         $code = $this->model->code; // 第三行,系统编号  
  94.         $audit = "我司审计处";    // 审计处名称,内容第一个变量  
  95.         $audited_object = " ";   // 被审计对象,内容第二个变量  
  96.   
  97.         $projectModel = Project::findOne(["id"=>$this->model->project_id]);  
  98.         $audit_users = $projectModel->getMembersToList();// 审计组成员  
  99.         $description = $this->model->description;       // 中间内容  
  100.   
  101.         $title = $projectModel->title;  // 项目名称,内容第三个变量  
  102.   
  103.         if($projectModel->audit_type == 1 && $projectModel->audit_users){  
  104.             // 如果是对人员审计并且有选择人员的话  
  105.             $useIds = explode(",",$projectModel->audit_users);  
  106.             $users = User::find()->where(["in","id",$useIds])->asArray()->all();  
  107.             $users = array_map(function ($user) {  
  108.                 return $user["true_name"]?$user["true_name"]:$user["username"];  
  109.             }, $users);  
  110.             $audited_object = implode("、",$users);  
  111.   
  112.         }else if($projectModel->audit_type == 0 && $projectModel->audit_department_id){  
  113.             // 0是部门  
  114.             $audited_object = Department::find()->select("title")->where(["id"=>$projectModel->audit_department_id])->scalar();  
  115.         }  
  116.   
  117.         if(!$description){  
  118.             // 默认内容  
  119.             $startTime = date("Y年m月d日",strtotime($projectModel->start_time));  
  120.             $description = "根据审计署工作安排,我办".$startTime."对".$audited_object."进行审计(".$title."),在审计实施前需要对相关情况进行调查。请予以配合,并提供有关资料(包括电子数据资料)和必要的工作条件。";// 通知书主体默认内容  
  121.         }  
  122.   
  123.         $audit_users_text = [];  
  124.         foreach ($audit_users as $k => $v){  
  125.             $item = $v["name"] . ":";  
  126.             foreach ($v["users"as $kk => $u){  
  127.                 $item .= $u["true_name"]?$u["true_name"]:$u["username"];  
  128.                 $kk == 0 ? ($item.=""):($item.=' ');  
  129.             }  
  130.             $audit_users_text[]["line"] = $item;  
  131.         }  
  132.   
  133.         $templateProcessor = new TemplateProcessor($this->template);  
  134.         $templateProcessor->setValue('company'$company);  
  135.         $templateProcessor->setValue('code'$code);  
  136.         $templateProcessor->setValue('audit'$audit);  
  137.         $templateProcessor->setValue('audited_object'$audited_object);  
  138.         $templateProcessor->setValue('title'$title);  
  139.         $templateProcessor->setValue('description'$description);  
  140.         $templateProcessor->setValue('today'date("Y年m月d日"));  
  141.         $templateProcessor->cloneBlock('audit_users', 0, true, false, $audit_users_text);  
  142.         return $templateProcessor->save();  
  143.     }  
  144.   
  145. }  

模板文件参考:

project_template_1.docx
文件类型: .docx bd6150a6cc5196acdb0f27a8b0e2657f.docx (58.76 KB)

 支持遍历,如果有错,检查是不是有空格影响的

 

2、根据图片以及文字,生成word

PHP Code复制内容到剪贴板
  1. <?php  
  2.   
  3. namespace plugins\education\components;  
  4.   
  5. use backend\modules\document\models\Cate;  
  6. use backend\modules\document\models\Exercise;  
  7. use common\models\Paper;  
  8. use PhpOffice\PhpWord\PhpWord;  
  9. use PhpOffice\PhpWord\Settings;  
  10. use PhpOffice\PhpWord\TemplateProcessor;  
  11. use yii\helpers\Json;  
  12.   
  13. class PaperDoc  
  14. {  
  15.     /** 
  16.      * @var string 试卷模板 
  17.      */  
  18.     public $template;  
  19.   
  20.     private $paper;  
  21.     private $phpWord;  
  22.     private $section;  
  23.     private $fileName;  
  24.   
  25.     public function __construct(Paper $paper$type = 1)  
  26.     {  
  27.         $this->paper = $paper;  
  28.         $this->phpWord = new PhpWord();  
  29.         $this->section = $this->phpWord->addSection();  
  30.         $this->fileName = tempnam(Settings::getTempDir(), 'PhpWord');  
  31.     }  
  32.   
  33.     public function buildHeader()  
  34.     {  
  35.         // , 'name' => 'Times New Roman'  
  36.         $titleStyle = ['bold' => true, 'size' => '15''line-height' => '2'];  
  37.         $subTitleStyle = ['bold' => true, 'size' => '18''name' => '黑体'];  
  38.         $titlePStyle = ['alignment' => 'center''textAlignment' => 'center'];  
  39.         $this->phpWord->addTitleStyle(1, $titleStyle$titlePStyle);  
  40.         $this->phpWord->addTitleStyle(2, $subTitleStyle$titlePStyle);  
  41.         $this->section->addTitle($this->paper->title, 1);  
  42.         $this->section->addTitle($this->paper->title_sub, 2);  
  43. //        $this->section->addText("考试范围:xxx;考试时间:{$this->paper->test_time}分钟;命题人:xxx", [], ['alignment' => 'center']);  
  44.         $this->section->addText("注意事项:");  
  45.         $this->section->addText("1.答题前填写好自己的姓名、班级、考号等信息:");  
  46.         $this->section->addText("2.请将答案正确填写在答题卡上:");  
  47.     }  
  48.   
  49.     private function buildContent()  
  50.     {  
  51.         $titleStyle = ['bold' => true, 'size' => '12''line-height' => '2''name' => '宋体 (中文标题)'];  
  52.         $titlePStyle = ['alignment' => 'center'];  
  53.         $this->section->addText("第I卷(选择题)"$titleStyle$titlePStyle);  
  54.         $questionTitlePStyle = [];  
  55.         $this->section->addText("单选题"$titleStyle$questionTitlePStyle);  
  56.         $exercises = $this->paper->exercise;  
  57.   
  58.         $radioQuestionsKey = 0;         // 选择题号  
  59.         foreach ($exercises as $key => $exercise) {  
  60.             if ($exercise->type == Exercise::TYPE_RADIO) {  
  61.                 // 选择题  
  62.                 $radioQuestionsKey++;  
  63.   
  64.                 // 题目  
  65.                 $questionNameText = $this->parseSimpleText($radioQuestionsKey ."、"$exercise->question_analysis);  
  66.   
  67.   
  68.                 // 写题目  
  69.                 $this->section->addTextBreak(1); //换一行  
  70.                 $this->writeComplexInline($questionNameText);  
  71.   
  72.                 // 选项  
  73.                 if ($exercise->options) {  
  74.   
  75.                     $this->section->addTextBreak(1); //换一行  
  76.                     $optionsSimpleText = "";  
  77.                     $op = Json::decode($exercise->options);  
  78.                     foreach ($op as $kk => $opt) {  
  79.                         $optionsSimpleText .= $opt["num"] . ". " . $opt["text"] . "      ";  
  80.                     }  
  81.                     $this->writeComplexInline($this->parseSimpleText($optionsSimpleText));  
  82.                 }  
  83.             }  
  84.         }  
  85.     }  
  86.   
  87.     private function parseSimpleText($text)  
  88.     {  
  89.         if (preg_match_all('/\[\[(.+?)\]\]/'$text$matches) !== false) {  
  90.             $images = $matches[1];  
  91.         } else {  
  92.             $images = [];  
  93.         }  
  94.         $result = [];  
  95.         foreach (preg_split("/\[\[|\]\]/"$textas $item) {  
  96.             if (in_array($item$images)) {  
  97.                 $result[] = [  
  98.                     'type' => 'image',  
  99.                     'content' => $item  
  100.                 ];  
  101.             } else {  
  102.                 $result[] = [  
  103.                     'type' => 'text',  
  104.                     'content' => $item  
  105.                 ];  
  106.             }  
  107.         }  
  108.         return $result;  
  109.     }  
  110.     private function writeComplexInline($complexText)  
  111.     {  
  112.         $pStyle = ['textAlignment' => 'center'];  
  113.         $inline = $this->section->addTextRun($pStyle);  
  114.         foreach ($complexText as $item) {  
  115.             if ($item['type'] == 'text') {  
  116.                 $inline->addText($item['content']);  
  117.             } else {  
  118.                 $inline->addImage($item['content']/*, ['height' => '50']*/);  
  119.             }  
  120.         }  
  121.     }  
  122.     public function build()  
  123.     {  
  124.         $this->buildHeader();  
  125.         $this->buildContent();  
  126.     }  
  127.     public function save($fileName)  
  128.     {  
  129.         $this->build();  
  130.         $this->phpWord->save($fileName);  
  131.     }  
  132.   
  133.     public function download()  
  134.     {  
  135.         $tmpFile = tempnam(Settings::getTempDir(), 'PhpWord');  
  136.         $this->save($tmpFile);  
  137.         return $tmpFile;  
  138.     }  
  139. }  

 

在上传题目的时候,需要对题目处理一下,如果是公式编辑器上传的base64图片,改为:

PHP Code复制内容到剪贴板
  1.     public function beforeSave($insert)  
  2.     {  
  3.         parent::beforeSave($insert);  
  4. //        if($insert) {  
  5. //            //执行添加的情况  
  6. //        } else {  
  7. //            //执行更新的情况  
  8. //        }  
  9.   
  10.         // 筛出所有图片  
  11.         $question = $this->processingEditorContent($this->question);  
  12.         // 筛出所有图片  
  13.         $answer = $this->processingEditorContent($this->answer);  
  14.   
  15.         $this->question_analysis = $question;   // 答案解析  
  16.         $this->answer_analysis = $answer;       // 答案解析  
  17. //        $this->save();  
  18.         return true;  
  19.     }  
  20.   
  21.     /** 
  22.      * 转换编辑器内容 
  23.      * @param $content 
  24.      * @return string|string[]|null 
  25.      */  
  26.     public static function ProcessingEditorContent($content){  
  27.         $text = preg_replace_callback('/<img.*?src="(.*?)".*?\/>/'function ($matches) {  
  28.             $image = $matches[1];  
  29.             if (preg_match('/^(data:\s*image\/(\w+);base64,)/'$image$result)) {  
  30.                 $path = date("Ymd")."/";  
  31.                 $url = Attachment::uploadFromBase64($path,$image);  
  32.                 return '[[' . $url . ']]';  
  33.             }else{  
  34.                 return '[[' . $image . ']]';  
  35.             }  
  36.         }, $content);  
  37.         $text = strip_tags($text);  
  38.         $text = str_replace(" ","",$text);  
  39.         return $text;  
  40.     }  
  41.   
  42.   
  43.     /** 
  44.      * 上传base64图片 
  45.      * @param $base64Img 
  46.      * @return mixed 
  47.      */  
  48.     private function uploadBase64Image($base64Img)  
  49.     {  
  50.         $path = date("Ymd")."/";  
  51.         $url= Attachment::uploadFromBase64($path,$base64Img);  
  52.         return $url;  
  53.     }  

 

  

 

本文来自于:http://www.yoyo88.cn/note/yoyocmf/508.html

上一篇 V2.0 - API
Powered by yoyo苏ICP备15045725号-1