使用 Google 脚本创建 and/or 更新邮件合并 Google 文档

Creating and/or update mail merge Google document using Google Script

我目前有一个 Google Sheet,其中包含要使用模板文件合并到 Google 文档中的数据行。每行都会根据模板的布局生成一个新的 Google 文档。我是 运行 的脚本当前将检查与 Google Sheet 行相关的文件(使用标题单元格匹配)是否已经存在。如果是这样,那么它将被删除并在其位置创建一个新文档,其中包含 Google Sheet 中所做的所有更改。如果不存在,将创建一个新文档。

不过,我现在需要从外部网站link到这些Google文件。使用当前脚本,这意味着如果对电子表格中的一行进行了更改,那么它的相应文档将被删除,并且 re-created 连同 URL,从而使网站 link 没用,需要手动修改

所以理想情况下,我想要实现的不是删除和 re-creating 文档,而是更新同一个文档(如果它已经存在)。这样,文档得到更新并且 URL 将始终相同。

目前的脚本是这样的

    /*###################################################################
 * Create multiple versions of a document based on Google Sheet Data
 * and a Google Doc Template (Mailmerge)
 *###################################################################
 */

/*###################################################################
 * Main run file sets up variables for insertion into the mailMerge() 
 * function. 
 */
function mergeSheetDoc() {
  const TEMPLATE_ID = '16YfyeDjGDp-88McAtLCQQyZ1xz4QX5zEKAS09SaLkJI';//Add your Google Doc template ID
  const SS_ID = '1C5gtJCSzHMuSz-oVWEItl2EUVRDwF5iH_RVr6BxLkOU'; // Add your Google Sheet ID
  const SHEET_NAME = "data"; // Add your Google Sheet Tab name
  const MAPPED = mappedDocToSheet; // Go to Map.gs to update
  const FILE_NAME = ["Titre de la formation"] // Header IDs from your Sheet. Change to make your own file name.
  
  docMerge(TEMPLATE_ID,SS_ID,SHEET_NAME,MAPPED, FILE_NAME);

}

/** ###################################################################
 * Merges data from a Google Sheet into a newly created doc based on a 
 * Google Doc template. 
 * 
 * param {string} templateID: The id from the Google Doc template. 
 * param {string} ssID: The id of the Google Sheet. 
 * param {string} sheetName: the name of the sheet tab you are referencing.
 * param {object} mapped: Object array of data you mapped from your Doc template against your Google Sheet headers
 * param {array} fileNameDara: An array of data used to generate the file name. 
 * param {string} rowLen: (optional) If you want to add a number rows to create your merged documents.  
 */ 
function docMerge(templateID,ssID, sheetName, mapped, fileNameData, rowLen = "auto"){
  //Get the Spreadsheet and sheet tab
  const ss = SpreadsheetApp.openById(ssID);
  const sheet = ss.getSheetByName(sheetName);
  
  //Get number of rows to process
  rowLen = (rowLen = "auto") ? getRowLen() : rowLen;
  
  //Gets the range of data in the sheet then grabs the values of the range
  const range = sheet.getRange(1,1,rowLen,sheet.getDataRange().getNumColumns());
  const matrix = range.getValues();
  
  // Searches the file mapped object and finds the corresponding number returns the column number in an array.
  const fileNameRows = getFileNameRows()
  
  
  //Loops through each row of the sheet grabbing the data from each row and putting it into a new doc.
  for(let i = 1; i < rowLen; i++){
    let row = matrix[i];
    //Get the title for the file.
    let fileName = buildFileName(row);
   
    //This query parameter will search for an exact match of the filename with Doc file type
  let params = "title='"+fileName+"' and mimeType = 'application/vnd.google-apps.document'"
    let files = DriveApp.searchFiles(params);

    if(files.hasNext()){
      while (files.hasNext()) {
        //Filename exist
        var file = files.next();


        //Create a new file
        var tmpDoc = DriveApp.getFileById(templateID).makeCopy("tempfile");
        //Update the file
        updateFileData(row, tmpDoc.getId());

        //Copy contents of the new file to the existing file
        copyFileData(tmpDoc.getId(), file.getId());

        //Delete temporary file
        tmpDoc.setTrashed(true);
      }
    }else{
      //Create a new file
      let newDoc = DriveApp.getFileById(templateID).makeCopy(fileName);
    
      updateFileData(row, newDoc.getId());
    }
  
  }
};

  function copyFileData(sourceDoc, destinationDoc){
   var srcParagraph = DocumentApp.openById(sourceDoc).getBody().getParagraphs();
   var dstParagraph = DocumentApp.openById(destinationDoc).getBody().getParagraphs();
   
   for(var i = 0; i<dstParagraph.length; i++){

      //Paragraph Index of description and suivantes information
      if(i==5||i==21){
        
        dstParagraph[i].editAsText().setText(srcParagraph[i].editAsText().getText());
        dstParagraph[i].setAttributes(srcParagraph[i].getAttributes());
        dstParagraph[i].setLeftToRight(true);
        dstParagraph[i].setIndentFirstLine(srcParagraph[i].getIndentFirstLine());
        dstParagraph[i].setIndentStart(srcParagraph[i].getIndentStart());
        dstParagraph[i].editAsText().setFontSize(srcParagraph[i].editAsText().getFontSize());

      }
   }
 }

此代码还引用了可以在此处找到的映射脚本

https://textuploader.com/18xic

解决方法摘要:

  • 如果文件名已经存在,创建一个临时文档来替换模板文档中的 textId 标签
  • 将临时文档的段落文本和属性复制到现有文档。

示例修改代码:

仅包含 modified/new 函数。

function docMerge(templateID,ssID, sheetName, mapped, fileNameData, rowLen = "auto"){
  //Get the Spreadsheet and sheet tab
  const ss = SpreadsheetApp.openById(ssID);
  const sheet = ss.getSheetByName(sheetName);
  
  //Get number of rows to process
  rowLen = (rowLen = "auto") ? getRowLen() : rowLen;
  
  //Gets the range of data in the sheet then grabs the values of the range
  const range = sheet.getRange(1,1,rowLen,sheet.getDataRange().getNumColumns());
  const matrix = range.getValues();
  
  // Searches the file mapped object and finds the corresponding number returns the column number in an array.
  const fileNameRows = getFileNameRows()
  
  
  //Loops through each row of the sheet grabbing the data from each row and putting it into a new doc.
  for(let i = 1; i < rowLen; i++){
    let row = matrix[i];
    //Get the title for the file.
    let fileName = buildFileName(row);
   
    //This query parameter will search for an exact match of the filename with Doc file type
  let params = "title='"+fileName+"' and mimeType = 'application/vnd.google-apps.document'"
    let files = DriveApp.searchFiles(params);

    if(files.hasNext()){
      while (files.hasNext()) {
        //Filename exist
        var file = files.next();


        //Create a new file
        var tmpDoc = DriveApp.getFileById(templateID).makeCopy("tempfile");
        //Update the file
        updateFileData(row, tmpDoc.getId());

        //Copy contents of the new file to the existing file
        copyFileData(tmpDoc.getId(), file.getId());

        //Delete temporary file
        tmpDoc.setTrashed(true);
      }
    }else{
      //Create a new file
      let newDoc = DriveApp.getFileById(templateID).makeCopy(fileName);
    
      updateFileData(row, newDoc.getId());
    }
  
  };
 
 function copyFileData(sourceDoc, destinationDoc){
   var srcParagraph = DocumentApp.openById(sourceDoc).getBody().getParagraphs();
   var dstParagraph = DocumentApp.openById(destinationDoc).getBody().getParagraphs();
   
   for(var i = 0; i<dstParagraph.length; i++){

      //Paragraph Index of description and suivantes information
      if(i==5||i==21){
        
        dstParagraph[i].editAsText().setText(srcParagraph[i].editAsText().getText());
        dstParagraph[i].setAttributes(srcParagraph[i].getAttributes());
        dstParagraph[i].setLeftToRight(true);
        dstParagraph[i].setIndentFirstLine(srcParagraph[i].getIndentFirstLine());
        dstParagraph[i].setIndentStart(srcParagraph[i].getIndentStart());
        dstParagraph[i].editAsText().setFontSize(srcParagraph[i].editAsText().getFontSize());

      }
   }
 }

它有什么作用?

  1. 如果找到文件名,根据电子表格中的更新信息创建一个临时文档
  2. 将特定段落从临时文档复制到现有文档
  3. 使用setTrashed()方法删除临时文档。