前端使用docxtemplater导出文档

在开发需求中,需要生成报告并导出,本文是前端利用docxtemplater实现替换模板文件并导出文档功能。

Docxtemplater是一个强大的库,可以帮助我们在前端生成Word文档。以下是如何在React项目中使用Docxtemplater导出Word文档的步骤。

1.安装所需的依赖

npm install docxtemplater pizzip docxtemplater-image-module-free

docxtemplater:这个插件可以通过预先写好的word,excel等文件模板生成对应带数据的文件;

pizzip:这个插件用来创建,读取或编辑.zip的文件

docxtemplater-image-module-free:需要导出图片的话需要这个插件

2.创建模板文件

在项目的public目录下创建一个templater.docx模板文件

然后在模板文件中使用模板语法,对需要替换的内容进行编写,例如:

其中的语法:

{%img} 图片

{#list}{/list} 循环、if判断

{#list}{/list}{^list}{/list} if else

{str} 文字

完整代码:

const docxData = {
    "projectName": project.projectName,
    "projectType": "",
    "index": [],
    "files": [],
    "MetalCaseTemplateDesignList": [],
    "CompositeCaseTemplateDesignList": [],
    "CommonNozzleTemplateDesignList": [],
    "FlexibleNozzleTemplateDesignList": [],
    "TemplateOverallList": [],
    "GrainTemplateDesignList": [],
    "IgniterTemplateDesignList": [],
    "BallisticMCList": [],
    imageUrls: []
  };
    const getData = async () => {
      const projectId = props.projectId;
      const token = localStorage.getItem('access_token');
      const url = `${baseHost}9002/project/manager/query/${projectId}`;

      try {
        const response = await fetch(url, {
          headers: {
            Authorization: `Bearer ${token}`,
          }
        })
        const data = await response.json();
        const projectConfigs = data.data.projectConfigs;
        const step2Configs = projectConfigs.filter((item: any) => item.configType === 'step2');
        console.log("step2Configs:", step2Configs);
        const resultValues = {
          case: '',
          nozzle: '',
          grain: ''
        };
        step2Configs.forEach((item: any) => {
          if (item.configKey.endsWith('-case')) {
            resultValues.case = item.configValue.replace("设计", "");
          } else if (item.configKey.endsWith('-nozzle')) {
            resultValues.nozzle = item.configValue.replace("设计工具", "");
          } else if (item.configKey.endsWith('-grain')) {
            resultValues.grain = item.configValue;
          }
        });
        const projectType = `${resultValues.case}${resultValues.nozzle}${resultValues.grain}发动机`;
        docxData.projectType = projectType;

        const step1Configs = projectConfigs.filter((item: any) => item.configType === 'step1');
        step1Configs.forEach((item: any) => {
          const configKey = item.configKey;
          const configValue = item.configValue;
          const match = configKey.match(/^(.*?)\((.*?)\)$/); //匹配configKey参数和单位
          if (match) {
            const param = match[1].trim();
            const unit = `(${match[2].trim()})`;
            const value = configValue;

            docxData.index.push({
              param,
              unit,
              value,
            });
          } else {
            docxData.index.push({
              param: configKey,
              unit: '',
              value: configValue
            });
          }
        });

        try {
          const res = await fetch(`${baseHost}9002/project/manager/project/node/cal/getAll?projectId=${projectId}`)
          const resData = await res.json();
          docxData.files = resData.data.map((item: any) => {
            const parsedFileContent = JSON.parse(item.fileContent);
            if (item.fileName === 'node_metal-case_template-design') {
              docxData.MetalCaseTemplateDesignList = parsedFileContent.request.paramList;
            }
            // if (item.fileName === 'node_srm-ballistic-mc') {
            //    const mcList = parsedFileContent.request.propMap;
            //   docxData.BallisticMCList = mcList.map((key, value) => {
            //     const param = key;
            //     const unit = "";
            //
            //     return{
            //       param,
            //       unit,
            //       value
            //     }
            //   })
            // }
            return {
              fileName: item.fileName,
              fileContent: parsedFileContent
            };
          });
          // console.log("获取结果json数据: " + JSON.stringify(docxData.files));

        } catch (error) {
          console.error('获取数据时发生错误:', error);
        }
      } catch (error) {
        console.error('获取数据时发生错误:', error);
      }

      //获取图片列表
      try {
        const response = await fetch(`${baseHost}9002/project/manager/project/node/img/list?projectId=${projectId}`)
        const responseData = await response.json();
        docxData.imageUrls = responseData.data;
        console.log("获取图片列表:" + docxData.imageUrls);
      } catch (error) {
        console.log("获取图片失败!", error);
      }

  }

    //导出结果文档
    const exportDoc = async () => {
      try {
        await getData()
      } catch (error) {
        console.error('获取数据时发生错误:', error);
      }
      try {
        const response = await fetch('/template.docx'); // 替换为你的模板文件路径
        const content = await response.arrayBuffer();
        const zip = new PizZip(content);

          // 动态生成模板数据对象
          const templateData = {
            projectName: docxData.projectName,
            projectType: docxData.projectType,
            index: docxData.index,
            MetalCaseTemplateDesignList: docxData.MetalCaseTemplateDesignList,
            CompositeCaseTemplateDesignList: docxData.CompositeCaseTemplateDesignList,
            CommonNozzleTemplateDesignList: docxData.CommonNozzleTemplateDesignList,
            FlexibleNozzleTemplateDesignList: docxData.FlexibleNozzleTemplateDesignList,
            TemplateOverallList: docxData.TemplateOverallList,
            GrainTemplateDesignList: docxData.GrainTemplateDesignList,
            IgniterTemplateDesignList: docxData.IgniterTemplateDesignList,
            BallisticMCList: docxData.BallisticMCList,
            OverallWizardDesign: false,
            TemplateOverall: false,
            GrainWizardDesign: false,
            GrainTemplateDesign: false,
            MetalCaseWizardDesign: false,
            MetalCaseTemplateDesign: false,
            MetalCaseStrengthCheck: false,
            InsulationWizardDesign: false,
            CompositeCaseWizardDesign: false,
            CompositeCaseTemplateDesign: false,
            CompositeCaseStrengthCheck: false,
            CommonNozzleWizardDesign: false,
            CommonNozzleTemplateDesign: false,
            FlexibleNozzleWizardDesign: false,
            FlexibleNozzleTemplateDesign: false,
            CircleIgniterWizardDesign: false,
            TubeIgniterWizardDesign: false,
            RocketIgniterWizardDesign: false,
            IgniterPowerSphereDesign: false,
            IgniterPowerRingDesign: false,
            IgniterPowerTabletDesign: false,
            IgniterPowerStarDesign: false,
            IgniterPowerWheelDesign: false,
            DualGrainBallisticZero: false,
            SingleGrainBallisticZero: false,
            SingleGrainBallisticOne: false,
            DualPulseBallisticZero: false,
            InsulationThickness: false,
            IspCalculate: false,
            BallisticMC: false,
            BurnRateRecognize: false,
            NozzleErosionRecognize: false,
            ExplodePc: false,
            ScrewDesign: false,
            FlangeDesign: false,
            SealingDesign: false,
            CommonTools: false,
            FuselageDesign: false,
            nodeSrmCommonToolsPc: false,
            nodeSrmCommonToolsCd: false,
            nodeSrmCommonToolsAb: false,
            nodeSrmCommonToolsRt: false,
            nodeSrmCommonToolsMb: false,
            nodeSrmCommonToolsPa: false,
            nodeSrmCommonToolsPe: false,
            nodeSrmCommonToolsIsp: false,
            nodeSrmCommonToolsF: false,
            nodeSrmCommonToolsR: false,
            nodeSrmCommonToolsEps: false,
            DualGrainBallisticZeroPImage: null,
            SingleGrainBallisticZeroPImage: null,
            SingleGrainBallisticOnePImage: null,
            DualPulseBallisticZeroPImage: null,
            MetalCaseWizardDesignModel: null,
            CompositeCaseWizardDesignModel: null,
            InsulationWizardDesignModel: null,
            GrainWizardDesignAb: null,
            GrainTemplateDesignModel: null,
            CircleIgniterWizardDesignModel: null,
            TubeIgniterWizardDesignModel: null,
            RocketIgniterWizardDesignModel: null,
            IgniterPowerSphereDesignAbImage: null,
            IgniterPowerSphereDesignQlistImage: null,
            IgniterPowerRingDesignAbImage: null,
            IgniterPowerRingDesignQlistImage: null,
            IgniterPowerTabletDesignAbImage: null,
            IgniterPowerTabletDesignQlistImage: null,
            IgniterPowerStarDesignAbImage: null,
            IgniterPowerStarDesignQlistImage: null,
            IgniterPowerWheelDesignAbImage: null,
            IgniterPowerWheelDesignQlistImage: null,
            CommonNozzleWizardDesignShapeImage: null,
            CommonNozzleWizardDesignFlowImage: null,
            CommonNozzleWizardDesignModelImage: null,
            FlexibleNozzleWizardDesignShapeImage: null,
            FlexibleNozzleWizardDesignFlowImage: null,
            FlexibleNozzleWizardDesignModel: null,
            BurnRateRecognizePImage: null,
            NozzleErosionRecognizeDtImage: null,
            FuselageDesignModel: null,
            BallisticMCP: null,
            BallisticMCP2: null,
          };

          //遍历 data.data 数组,提取每个文件的内容并生成对应的占位符数据
        for (const item of docxData.files) {
            const { fileName, fileContent } = item;
            const prefix = fileName.replace(/\.json$/, '').replace(/[^a-zA-Z0-9]/g, '_'); // 生成前缀

            if (item.fileName === 'node_srm-interactive-overall') {
              templateData.OverallWizardDesign = true;
            }
            if (item.fileName === 'node_srm-template-overall') {
              templateData.TemplateOverall = true;
            }
            if (item.fileName === 'node_grain_interactive-design') {
              templateData.GrainWizardDesign = true;
              const urls = docxData.imageUrls.filter(url => url.includes('node_grain_interactive-design'))
              const imageBase = await fetchImageAsBase64(urls);
              templateData.GrainWizardDesignAb = imageBase[0];
            }
            if (item.fileName === 'node_grain_template-design') {
              templateData.GrainTemplateDesign = true;
              const urls = docxData.imageUrls.filter(url => url.includes('node_grain_template-design'))
              const imageBase = await fetchImageAsBase64(urls);
              templateData.GrainTemplateDesignModel = imageBase[0];
            }
            if (item.fileName === 'node_metal-case_interactive-design') {
              templateData.MetalCaseWizardDesign = true;
              const urls = docxData.imageUrls.filter(url => url.includes('node_metal-case_interactive-design'))
              const imageBase = await fetchImageAsBase64(urls);
              templateData.MetalCaseWizardDesignModel = imageBase[0];
            }
            if (item.fileName === 'node_metal-case_template-design') {
              templateData.MetalCaseTemplateDesign = true;
            }
            if (item.fileName === 'node_metal-case_strength-check') {
              templateData.MetalCaseStrengthCheck = true;
            }
            if (item.fileName === 'node_insulation_wizard_design') {
              templateData.InsulationWizardDesign = true;
              const urls = docxData.imageUrls.filter(url => url.includes('node_insulation_wizard_design'))
              const imageBase = await fetchImageAsBase64(urls);
              templateData.InsulationWizardDesignModel = imageBase[0];
            }
            if (item.fileName === 'node_composite-case_interactive-design') {
              templateData.CompositeCaseWizardDesign = true;
              const urls = docxData.imageUrls.filter(url => url.includes('node_composite-case_interactive-design'))
              const imageBase = await fetchImageAsBase64(urls);
              templateData.CompositeCaseWizardDesignModel = imageBase[0];
            }
            if (item.fileName === 'node_composite-case_template-design') {
              templateData.CompositeCaseTemplateDesign = true;
            }
            if (item.fileName === 'node_composite-case_strength-check') {
              templateData.CompositeCaseStrengthCheck = true;
            }
            if (item.fileName === 'node_common-nozzle-interactive-design') {
              templateData.CommonNozzleWizardDesign = true;
              const urls = docxData.imageUrls.filter(url => url.includes('node_common-nozzle-interactive-design'))
              const imageBase = await fetchImageAsBase64(urls);
              templateData.CommonNozzleWizardDesignShapeImage = imageBase[2];
              templateData.CommonNozzleWizardDesignFlowImage = imageBase[0];
              templateData.CommonNozzleWizardDesignModelImage = imageBase[1];
            }
            if (item.fileName === 'node_common-nozzle-template-design') {
              templateData.CommonNozzleTemplateDesign = true;
            }
            if (item.fileName === 'node_composite-case_strength-check') {
              templateData.CompositeCaseStrengthCheck = true;
            }
            if (item.fileName === 'node_flexible-nozzle-interactive-design') {
              templateData.FlexibleNozzleWizardDesign = true;
              const urls = docxData.imageUrls.filter(url => url.includes('node_flexible-nozzle-interactive-design'))
              const imageBase = await fetchImageAsBase64(urls);
              templateData.FlexibleNozzleWizardDesignShapeImage = imageBase[2];
              templateData.FlexibleNozzleWizardDesignFlowImage = imageBase[0];
              templateData.FlexibleNozzleWizardDesignModel = imageBase[1];
            }
            if (item.fileName === 'node_flexible-nozzle-template-design') {
              templateData.FlexibleNozzleTemplateDesign = true;
            }
            if (item.fileName === 'node_circle-igniter-interactive-design') {
              templateData.CircleIgniterWizardDesign = true;
              const urls = docxData.imageUrls.filter(url => url.includes('node_circle-igniter-interactive-design'))
              const imageBase = await fetchImageAsBase64(urls);
              templateData.CircleIgniterWizardDesignModel = imageBase[0];
            }
            if (item.fileName === 'node_tube-igniter-interactive-design') {
              templateData.TubeIgniterWizardDesign = true;
              const urls = docxData.imageUrls.filter(url => url.includes('node_tube-igniter-interactive-design'))
              const imageBase = await fetchImageAsBase64(urls);
              templateData.TubeIgniterWizardDesignModel = imageBase[0];
            }
            if (item.fileName === 'node_rocket-igniter-interactive-design') {
              templateData.RocketIgniterWizardDesign = true;
              const urls = docxData.imageUrls.filter(url => url.includes('node_rocket-igniter-interactive-design'))
              const imageBase = await fetchImageAsBase64(urls);
              templateData.RocketIgniterWizardDesignModel = imageBase[0];
            }
            if (item.fileName === 'node_igniter-powder-sphere-design') {
              templateData.IgniterPowerSphereDesign = true;
              const urls = docxData.imageUrls.filter(url => url.includes('node_igniter-powder-sphere-design'))
              const imageBase = await fetchImageAsBase64(urls);
              templateData.IgniterPowerSphereDesignAbImage = imageBase[0];
              templateData.IgniterPowerSphereDesignQlistImage = imageBase[1];
            }
            if (item.fileName === 'node_igniter-powder-ring-design') {
              templateData.IgniterPowerRingDesign = true;
              const urls = docxData.imageUrls.filter(url => url.includes('node_igniter-powder-ring-design'))
              const imageBase = await fetchImageAsBase64(urls);
              templateData.IgniterPowerRingDesignAbImage = imageBase[0];
              templateData.IgniterPowerRingDesignQlistImage = imageBase[1];
            }
            if (item.fileName === 'node_igniter-powder-tablet-design') {
              templateData.IgniterPowerTabletDesign = true;
              const urls = docxData.imageUrls.filter(url => url.includes('node_igniter-powder-tablet-design'))
              const imageBase = await fetchImageAsBase64(urls);
              templateData.IgniterPowerTabletDesignAbImage = imageBase[0];
              templateData.IgniterPowerTabletDesignQlistImage = imageBase[1];
            }
            if (item.fileName === 'node_igniter-powder-star-design') {
              templateData.IgniterPowerStarDesign = true;
              const urls = docxData.imageUrls.filter(url => url.includes('node_igniter-powder-star-design'))
              const imageBase = await fetchImageAsBase64(urls);
              templateData.IgniterPowerStarDesignAbImage = imageBase[0];
              templateData.IgniterPowerStarDesignQlistImage = imageBase[1];
            }
            if (item.fileName === 'node_igniter-powder-wheel-design') {
              templateData.IgniterPowerWheelDesign = true;
              const urls = docxData.imageUrls.filter(url => url.includes('node_igniter-powder-wheel-design'))
              const imageBase = await fetchImageAsBase64(urls);
              templateData.IgniterPowerWheelDesignAbImage = imageBase[0];
              templateData.IgniterPowerWheelDesignQlistImage = imageBase[1];
            }
            if (item.fileName === 'node_single-grain_ballistic-one') {
              templateData.SingleGrainBallisticOne = true;
              const urls = docxData.imageUrls.filter(url => url.includes('node_single-grain_ballistic-one'))
              const imageBase = await fetchImageAsBase64(urls);
              templateData.SingleGrainBallisticOnePImage = imageBase[0];
            }
            if (item.fileName === 'node_single-grain_ballistic-zero') {
              templateData.SingleGrainBallisticZero = true;
              const urls = docxData.imageUrls.filter(url => url.includes('node_single-grain_ballistic-zero'))
              const imageBase = await fetchImageAsBase64(urls);
              templateData.SingleGrainBallisticZeroPImage = imageBase[0];
            }
            if (item.fileName === 'node_dual-grain_ballistic-zero') {
              templateData.DualGrainBallisticZero = true;
              const urls = docxData.imageUrls.filter(url => url.includes('node_dual-grain_ballistic-zero'))
              const imageBase = await fetchImageAsBase64(urls);
              templateData.DualGrainBallisticZeroPImage = imageBase[0];
            }
            if (item.fileName === 'node_dual-pulse_ballistic-zero') {
              templateData.DualPulseBallisticZero = true;
              const urls = docxData.imageUrls.filter(url => url.includes('node_dual-pulse_ballistic-zero'))
              const imageBase = await fetchImageAsBase64(urls);
              templateData.DualPulseBallisticZeroPImage = imageBase[0];
            }
            if (item.fileName === 'node_srm-insulation-thickness') {
              templateData.InsulationThickness = true;
            }
            if (item.fileName === 'node_srm-isp-calculate') {
              templateData.IspCalculate = true;
            }
            if (item.fileName === 'node_srm-ballistic-mc') {
              templateData.BallisticMC = true;
              const urls = docxData.imageUrls.filter(url => url.includes('node_srm-ballistic-mc'))
              const imageBase = await fetchImageAsBase64(urls);
              templateData.BallisticMCP = imageBase[0];
              templateData.BallisticMCP2 = imageBase[1];
            }
            if (item.fileName === 'node_srm-burn-rate-recognize') {
              templateData.BurnRateRecognize = true;
              const urls = docxData.imageUrls.filter(url => url.includes('node_srm-burn-rate-recognize'))
              const imageBase = await fetchImageAsBase64(urls);
              templateData.BurnRateRecognizePImage = imageBase[0];
            }
            if (item.fileName === 'node_srm-nozzle-erosion-recognize') {
              templateData.NozzleErosionRecognize = true;
              const urls = docxData.imageUrls.filter(url => url.includes('node_srm-nozzle-erosion-recognize'))
              const imageBase = await fetchImageAsBase64(urls);
              templateData.NozzleErosionRecognizeDtImage = imageBase[0];
            }
            if (item.fileName === 'node_srm-explode-pc') {
              templateData.ExplodePc = true;
            }
            if (item.fileName === 'node_srm-screw-design') {
              templateData.ScrewDesign = true;
            }
            if (item.fileName === 'node_srm-flange-design') {
              templateData.FlangeDesign = true;
            }
            if (item.fileName === 'node_srm-sealing-design') {
              templateData.SealingDesign = true;
            }
            if (item.fileName === 'node_srm-common-tools') {
              templateData.CommonTools = true;
            }
            if (item.fileName === 'node_srm-fuselage-design') {
              templateData.FuselageDesign = true;
              const urls = docxData.imageUrls.filter(url => url.includes('node_srm-fuselage-design'))
              const imageBase = await fetchImageAsBase64(urls);
              templateData.FuselageDesignModel = imageBase[0];
            }

            Object.keys(fileContent.request).forEach(key => {
              templateData[`${prefix}_${key}`] = fileContent.request[key];
            });

            // 添加响应参数
            Object.keys(fileContent.response).forEach(key => {
              templateData[`${prefix}_${key}`] = fileContent.response[key];
            });
          };

          // 手动解析模板文件中的占位符
          const templatePlaceholders = extractPlaceholdersFromTemplate(content);

          // 确保模板中定义的所有占位符在 templateData 中都有对应的键值对
          templatePlaceholders.forEach(placeholder => {
            if (!templateData.hasOwnProperty(placeholder)) {
              templateData[placeholder] = ''; // 如果某个占位符在 templateData 中不存在,则赋值为空字符串
            }
          });

        // function getImage(tagValue, tagName) {
        //   return templateData[tagName]; // 直接返回模板数据中的属性
        // }


        // // 加载图片模块
        // const imageModule = new ImageModule({
        //   centered: true, // 是否居中对齐图片
        //   imageSource: 'base64', // 使用 Base64 格式
        //   getImage: getImage,
        //   getSize: (imgBuffer, tagValue) => {
        //     // 处理 tagValue 为 undefined 或空的情况
        //     if (!tagValue) {
        //       console.error("图片对象未定义,使用默认尺寸");
        //       return [400, 300]; // 返回默认尺寸
        //     }
        //     return [tagValue.width, tagValue.height];
        //   },
        // });

        const base64Regex =
          /^(?:data:)?image\/(png|jpg|jpeg|svg|svg\+xml);base64,/;

        const validBase64 =
          /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;

        function base64Parser(tagValue) {
          if (
            typeof tagValue !== "string" ||
            !base64Regex.test(tagValue)
          ) {
            return false;
          }

          const stringBase64 = tagValue.replace(base64Regex, "");

          if (!validBase64.test(stringBase64)) {
            throw new Error(
              "Error parsing base64 data, your data contains invalid characters"
            );
          }

          // For nodejs, return a Buffer
          if (typeof Buffer !== "undefined" && Buffer.from) {
            return Buffer.from(stringBase64, "base64");
          }

          // For browsers, return a string (of binary content) :
          const binaryString = window.atob(stringBase64);
          const len = binaryString.length;
          const bytes = new Uint8Array(len);
          for (let i = 0; i < len; i++) {
            const ascii = binaryString.charCodeAt(i);
            bytes[i] = ascii;
          }
          return bytes.buffer;
        }

        const imageOptions = {
          getImage(tagValue) {
            return base64Parser(tagValue);
          },
          getSize(img, tagValue, tagName, context) {
            return [600, 420];
          },
        };

        // 初始化 Docxtemplater 并添加图片模块
        const doc = new Docxtemplater(zip, {
          paragraphLoop: true,
          linebreaks: true,
          modules: [new ImageModule(imageOptions)],
        });

          try {
            // 渲染模板
            doc.render(templateData);
          } catch (error) {
            console.error('渲染模板时出错:', error);
            return;
          }

          // 获取渲染后的文件内容
          const out = doc.getZip().generate({
            type: 'blob',
            mimeType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
          });

          // 创建一个链接元素用于下载
          const link = document.createElement('a');
          link.href = URL.createObjectURL(out);
          link.download = '发动机方案设计报告.docx'; // 设置下载文件名
          document.body.appendChild(link);
          link.click();
          document.body.removeChild(link);
        } catch(error) {
          console.error('加载模板文件时出错:', error);
        };
    }

// 获取所有图片的 Blob 数据
  async function fetchImageAsBase64(imageUrls) {
    const imageBase64 = [];
    for (const url of imageUrls) {
      try {
        const response = await fetch(url);
        if (!response.ok) {
          throw new Error('图片获取失败');
        }
        const blob = await response.blob(); // 使用 blob 获取图片数据
        const base64 = await blobToBase64(blob); // 转换为 Base64

        imageBase64.push(base64);
        // console.log("base64: " + base64);

        // // 动态获取图片尺寸
        // const size = await getImageSize(blob); // 需要实现 getImageSize 函数
        // imageBase64.push({
        //   src: base64,
        //   width: size.width || 400,
        //   height: size.height || 300,
        // });

      } catch (error) {
        console.error(`获取图片失败:${url}`, error);
        imageBase64.push(null); // 保留占位符,方便后续处理
      }
    }
    return imageBase64;
  }
  //
  // function getImageSize(blob) {
  //   return new Promise((resolve) => {
  //     const img = new Image();
  //     img.onload = () => {
  //       resolve({ width: img.width, height: img.height });
  //     };
  //     img.src = URL.createObjectURL(blob);
  //   });
  // }

  // 将 Blob 转换为 Base64 的辅助函数
  function blobToBase64(blob) {
    return new Promise((resolve, reject) => {
      const reader = new FileReader();
      reader.onload = () => resolve(reader.result);
      reader.onerror = reject;
      reader.readAsDataURL(blob);
    });
  }

  //手动解析模板文件中的占位符
  function extractPlaceholdersFromTemplate(content) {
    const zip = new PizZip(content);
    const doc = new Docxtemplater(zip, {
      paragraphLoop: true,
      linebreaks: true
    });

    // 获取模板文件中的所有 XML 文件
    const xmlFiles = doc.getZip().file(/\.xml$/);

    const placeholders = new Set();

    xmlFiles.forEach(file => {
      let xmlContent;

      // 检查文件内容的类型
      if (typeof file._data === 'string') {
        // 如果是字符串,直接使用
        xmlContent = file._data;
      } else if (file._data instanceof Uint8Array) {
        // 如果是 Uint8Array,使用 TextDecoder 转换为字符串
        const decoder = new TextDecoder('utf-8');
        xmlContent = decoder.decode(file._data);
      } else {
        // 如果文件内容类型未知,跳过该文件
        console.warn('无法解析文件内容:', file.name);
        return;
      }

      // 使用正则表达式提取占位符
      const matches = xmlContent.match(/{[^}]+}/g);
      if (matches) {
        matches.forEach(match => {
          const placeholder = match.slice(1, -1); // 去掉大括号
          placeholders.add(placeholder);
        });
      }
    });

    return Array.from(placeholders);
  }

最后通过创建a标签下载文档,最终文档展示:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值