在开发合同管理中,需要使用到此功能,就是替换合同模板内的一些字符串,如:甲乙方名称,金额日期等,合同内容不变。效果如下:
使用docx-preview 好处是只预览不可编辑内容。
前端vue
import { renderAsync } from 'docx-preview';// 替换文件内容
function onWordSave(){axios({method: "post",responseType: "blob", // 因为是流文件,所以要指定blob类型url: Http.getBaseURL() + "/contract/Index/setWord", // 自己的服务器,提供的一个word下载文件接口data:{'fieldData':select_field.value,'tem_id':form.value.tem_id}}).then(response => {if (response.status === 200) {// 检查Content-Type是否是预期的类型const contentType = response.headers['content-type'] || response.headers['Content-Type'];if (contentType === 'application/vnd.openxmlformats-officedocument.wordprocessingml.document') {// 数据正确,处理blob数据ElMessage({type:'success', message:'替换模板内容成功'})file_url.value = response.data}else {// 数据类型不匹配ElMessage({type:'error', message: '模板内容替换失败'})}} else {// 响应状态不是200ElMessage({type:'error', message: 'failed to open stream: No such file or directory'})}}).catch(error => {// 请求失败,处理错误ElMessage({type:'error', message:error})});
}
// 预览文件
function docxRender() {ContractFileRef.value.innerHTML = ''; // 清空之前的内容if(file_url.value != ''){let blob = new Blob([file_url.value]); // 将 OSS 文件转为 Blob 对象// 渲染文档并获取内容控件renderAsync(blob, ContractFileRef.value);}
}
后端 控制器方法:
/*** @名称:模板内容替换* User: congxion* Date: 2024/10/31 16:44*/public function setWord(){try{$param = $this->request->param();if(empty($param['tem_id'])){return jsonError('请选择合同模板');}if(empty($param['fieldData'])){return jsonError('没有可替换的内容');}return $res = ContractService::setWord($param['fieldData'], $param['tem_id']);}catch(\Exception $e){return jsonError($e->getMessage());}}
后端逻辑层方法
public static function setWord($data,$met_id){$template = Db::name('template')->where(['id'=>$met_id])->find();if(empty($template['file_url'])){throw new Exception('没有模板文件');}// 加载Word文档$template_path = public_path().'storage/'.$template['file_url'];if(!file_exists($template_path)){throw new Exception(public_path().'storage/'.$template['file_url']);}//读取模版word的版本最好是docx,要不然可能会读取不了$templateProcessor = new TemplateProcessor($template_path);$data_str = "";foreach ($data as $k => $v) { //读取模版word的版本最好是docx,要不然可能会读取不了$newname = !empty($v['newname']) ? $v['newname']: '';$templateProcessor->setValue($v['key'], $newname); //替换模板中的变量,对应word里的 ${xxxx}$data_str .= $v['key'].$newname;}$save_name = GetRandStr(5).'_'.time() . '.docx'; //保存的文件名$return_path = 'storage/temword/'.date('Ymd').'/'; //保存的文件路径$savepath = public_path() . $return_path;if (!is_dir($savepath)) {@mkdir($savepath,0777);}$templateProcessor->saveAs($savepath.$save_name); //将内容保存到生成新的word中$data_md5 = md5($met_id.$data_str);// 缓存文件名Cache::set($data_md5, $return_path.$save_name, 3600);// 或输出到浏览器header('Content-Description: File Transfer');header('Content-Type: application/vnd.openxmlformats-officedocument.wordprocessingml.document');header('Content-Disposition: attachment; filename="output.docx"');header('Cache-Control: must-revalidate');header('Pragma: public');header('Expires: 0');readfile($savepath.$save_name);exit;}
接口返回的是文件流,前端插件可直接使用