下载Shiny中的嵌套DT表

问题描述 投票:1回答:1

我在我的Shiny应用里有一个嵌套的DT表。子表是一个列表,被传递并渲染到表中。当我通过 DT 的内置方法添加下载按钮时,输出包含父表数据,然后是一个列表,其中包括 [Object][Object].. 而不是实际的子数据。

我是要自己写一个下载按钮的方法,而不是用DT的方法,还是有DT的原创方法可以帮助解决这个问题?还没找到解决办法。

数据

Parent
structure(list(Market = c("ABILENE, TX", "AKRON, OH"), `SQAD CPP ($)` = c(10, 
49), `SQAD CPM ($)` = c(22, 30), `Override CPP ($)` = c(0, 0), 
    `Override CPM ($)` = c(0, 0)), .Names = c("Market", "SQAD CPP ($)", 
"SQAD CPM ($)", "Override CPP ($)", "Override CPM ($)"), row.names = c(NA, 
-2L), class = "data.frame")

Child
structure(list(Market = c("ABILENE, TX", "ABILENE, TX", "ABILENE, TX", 
"ABILENE, TX", "ABILENE, TX", "ABILENE, TX", "ABILENE, TX", "ABILENE, TX", 
"ABILENE, TX", "AKRON, OH", "AKRON, OH", "AKRON, OH", "AKRON, OH", 
"AKRON, OH", "AKRON, OH", "AKRON, OH", "AKRON, OH", "AKRON, OH"
), Daypart = c(" Podcast", " Streaming/Digital Audio", "Afternoon Drive", 
"Daytime", "Evening", "Mon-Fri Average", "Mon-Sun Average", "Morning Drive", 
"Weekend", " Podcast", " Streaming/Digital Audio", "Afternoon Drive", 
"Daytime", "Evening", "Mon-Fri Average", "Mon-Sun Average", "Morning Drive", 
"Weekend"), `Mix (%)` = c(10L, 10L, 10L, 10L, 10L, 10L, 5L, 15L, 
10L, 10L, 10L, 10L, 10L, 10L, 10L, 5L, 15L, 10L), `Spot:60 (%)` = c(4, 
4, 4, 4, 4, 4, 2, 6, 4, 4, 4, 4, 4, 4, 4, 2, 6, 4), `Spot:30 (%)` = c(6, 
6, 6, 6, 6, 6, 3, 9, 6, 6, 6, 6, 6, 6, 6, 3, 9, 6), `SQAD CPP ($)` = c(10, 
6, 27, 31, 44, 32, 31, 26, 26, 34, 21, 170, 156, 112, 151, 136, 
177, 95), `SQAD CPM ($)` = c(21, 13, 57.6, 64.8, 93.6, 68.4, 
64.8, 54, 54, 21, 13, 104.5, 96.1, 69, 93, 83.6, 108.7, 58.5), 
    `Override CPP ($)` = c(10, 6, 27, 31, 44, 32, 31, 26, 26, 
    34, 21, 170, 156, 112, 151, 136, 177, 95), `Override CPM ($)` = c(21, 
    13, 57.63, 64.83, 93.64, 68.43, 64.83, 54.03, 54.03, 21, 
    13, 104.49, 96.13, 68.96, 92.99, 83.59, 108.67, 58.51), population = c(47200L, 
    47200L, 47200L, 47200L, 47200L, 47200L, 47200L, 47200L, 47200L, 
    162700L, 162700L, 162700L, 162700L, 162700L, 162700L, 162700L, 
    162700L, 162700L), slider_60s = c(0.4, 0.4, 0.4, 0.4, 0.4, 
    0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 
    0.4), slider_30s = c(0.6, 0.6, 0.6, 0.6, 0.6, 0.6, 0.6, 0.6, 
    0.6, 0.6, 0.6, 0.6, 0.6, 0.6, 0.6, 0.6, 0.6, 0.6)), .Names = c("Market", 
"Daypart", "Mix (%)", "Spot:60 (%)", "Spot:30 (%)", "SQAD CPP ($)", 
"SQAD CPM ($)", "Override CPP ($)", "Override CPM ($)", "population", 
"slider_60s", "slider_30s"), class = "data.frame", row.names = c(NA, 
-18L))

编码

# Bind the market level and mix breakout data together for the final table
market_mix_table <- reactive({
  # Take a dependency on input$goButton
  input$goButton

  isolate({
    markets <- market_costings_gross_net()
    mix_breakout <- mix_breakout_digital_elements()
    # Make the dataframe
    # This must be met length(children) == nrow(dat)
    Dat <- NestedData(
      dat = markets,
      children = split(mix_breakout, mix_breakout$Market)
    )
    return(Dat)
  })
})

# Render the table
output$daypartTable <- DT::renderDataTable({
  Server = FALSE
  # Whether to show row names (set TRUE or FALSE)
  rowNames <- FALSE
  colIdx <- as.integer(rowNames)
  # The data
  Dat <- market_mix_table()
  parentRows <- which(Dat[,1] != "")
  callback_js = JS(
    "var ok = true;",
    "function onUpdate(updatedCell, updatedRow, oldValue) {",
    "  var column = updatedCell.index().column;",
    "  if(column === 8){",
    "    ok = false;",
    "  }else if(column === 7){",
    "    ok = true;",
    "  }",
    "}",
    sprintf("var parentRows = [%s];", toString(parentRows-1)),
    sprintf("var j0 = %d;", colIdx),
    "var nrows = table.rows().count();",
    "for(var i=0; i < nrows; ++i){",
    "  if(parentRows.indexOf(i) > -1){",
    "    table.cell(i,j0).nodes().to$().css({cursor: 'pointer'});",
    "  }else{",
    "    table.cell(i,j0).nodes().to$().removeClass('details-control');",
    "  }",
    "}",
    "",
    "// make the table header of the nested table",
    "var format = function(d, childId){",
    "  if(d != null){",
    "    var html = ",
    "      '<table class=\"display compact hover\" ' + ",
    "      'style=\"padding-left: 30px;\" id=\"' + childId + '\"><thead><tr>';",
    "    for(var key in d[d.length-1][0]){",
    "      html += '<th>' + key + '</th>';",
    "    }",
    "    html += '</tr></thead><tfoot><tr>'",
    "    for(var key in d[d.length-1][0]){",
    "      html += '<th></th>';",
    "    }",
    "    return html + '</tr></tfoot></table>';",
    "  } else {",
    "    return '';",
    "  }",
    "};",
    "",
    "// row callback to style the rows of the child tables",
    "var rowCallback = function(row, dat, displayNum, index){",
    "  if($(row).hasClass('odd')){",
    "    $(row).css('background-color', 'white');",
    "    $(row).hover(function(){",
    "      $(this).css('background-color', 'lightgreen');",
    "    }, function() {",
    "      $(this).css('background-color', 'white');",
    "    });",
    "  } else {",
    "    $(row).css('background-color', 'white');",
    "    $(row).hover(function(){",
    "      $(this).css('background-color', 'lightblue');",
    "    }, function() {",
    "      $(this).css('background-color', 'white');",
    "    });",
    "  }",
    "};",
    "",
    "// header callback to style the header of the child tables",
    "var headerCallback = function(thead, data, start, end, display){",
    "  $('th', thead).css({",
    "    'color': 'black',",
    "    'background-color': 'white'",
    "  });",
    "};",
    "",
    "// make the datatable",
    "var format_datatable = function(d, childId, rowIdx){",
    "  // footer callback to display the totals",
    "  // and update the parent row",
    "  var footerCallback = function(tfoot, data, start, end, display){",
    "    $('th', tfoot).css('background-color', '#F5F2F2');",
    "    var api = this.api();",
    "// update the Override CPM when the Override CPP is changed",
    "    var col_override_cpp = api.column(7).data();",
    "    var col_population = api.column(9).data();",
    "    if(ok){",
    "      for(var i = 0; i < col_override_cpp.length; i++){",
    "        api.cell(i,8).data(((parseInt(col_override_cpp[i])*100)/(parseInt(col_population[i])/1000)).toFixed(0));",
    "      }",
    "    }",
    "// update the Override CPP when the Override CPM is changed",
    "    var col_override_cpm = api.column(8).data();",
    "    for(var i = 0; i < col_override_cpm.length; i++){",
    "      api.cell(i,7).data(((parseInt(col_override_cpm[i])*parseInt(col_population[i])/1000)/(100)).toFixed(0));",
    "    }",
    "// Update the spot mixes",
    "    var col_mix_percentage = api.column(2).data();",
    "    var col_mix60_mix30 = api.column(10).data();",
    "    var col_mix30_mix15 = api.column(11).data();",
    "    for(var i = 0; i < col_mix_percentage.length; i++){",
    "      api.cell(i,3).data((parseFloat(col_mix_percentage[i])*parseFloat(col_mix60_mix30[i])).toFixed(1));",
    "      api.cell(i,4).data((parseFloat(col_mix_percentage[i])*parseFloat(col_mix30_mix15[i])).toFixed(1));",
    "    }",
    "// Make the footer sums",
    "    api.columns().eq(0).each(function(index){",
    "      if(index == 0) return $(api.column(index).footer()).html('Mix Total');",
    "      var coldata = api.column(index).data();",
    "      var total = coldata",
    "          .reduce(function(a, b){return parseInt(a) + parseInt(b)}, 0);",
    "      if(index == 3 || index == 4 ||index == 5 || index == 6 || index==7 || index==8) {",
    "        $(api.column(index).footer()).html('');",
    "      } else {",
    "        $(api.column(index).footer()).html(total);",
    "      }",
    "      if(total == 100) {",
    "        $(api.column(index).footer()).css({'color': 'green'});",
    "      } else {",
    "        $(api.column(index).footer()).css({'color': 'red'});",
    "      }",
    "    })",
    "  // update the parent row",
    "    var col_share = api.column(2).data();",
    "    var col_CPP = api.column(5).data();",
    "    var col_CPM = api.column(6).data();",
    "    var col_Historical_CPP = api.column(7).data();",
    "    var col_Historical_CPM = api.column(8).data();",
    "    var CPP = 0, CPM = 0, Historical_CPP = 0, Historical_CPM = 0;",
    "    for(var i = 0; i < col_share.length; i++){",
    "      CPP += (parseInt(col_share[i])*parseInt(col_CPP[i]).toFixed(0));",
    "      CPM += (parseInt(col_share[i])*parseInt(col_CPM[i]).toFixed(0));",
    "      Historical_CPP += (parseInt(col_share[i])*parseInt(col_Historical_CPP[i]).toFixed(0));",
    "      Historical_CPM += (parseInt(col_share[i])*parseInt(col_Historical_CPM[i]).toFixed(0));",
    "    }",
    "    table.cell(rowIdx, j0+2).data((CPP/100).toFixed(2));",
    "    table.cell(rowIdx, j0+3).data((CPM/100).toFixed(2));",
    "    table.cell(rowIdx, j0+4).data((Historical_CPP/100).toFixed(2));",
    "    table.cell(rowIdx, j0+5).data((Historical_CPM/100).toFixed(2));",
    "  }",
    "  var dataset = [];",
    "  var n = d.length - 1;",
    "  for(var i = 0; i < d[n].length; i++){",
    "    var datarow = $.map(d[n][i], function (value, index) {",
    "      return [value];",
    "    });",
    "    dataset.push(datarow);",
    "  }",
    "  var id = 'table#' + childId;",
    "  if (Object.keys(d[n][0]).indexOf('_details') === -1) {",
    "    var subtable = $(id).DataTable({",
    "                 'data': dataset,",
    "                 'autoWidth': true,",
    "                 'deferRender': true,",
    "                 'info': false,",
    "                 'lengthChange': false,",
    "                 'ordering': d[n].length > 1,",
    "                 'order': [],",
    "                 'paging': true,",
    "                 'scrollX': false,",
    "                 'scrollY': false,",
    "                 'searching': false,",
    "                 'sortClasses': false,",
    "                 'pageLength': 50,",
    "                 'rowCallback': rowCallback,",
    "                 'headerCallback': headerCallback,",
    "                 'footerCallback': footerCallback,",
    "                 'columnDefs': [",
    "                  {targets: [0, 9, 10, 11], visible: false},",
    "                  {targets: '_all', className: 'dt-center'}",
    "                 ]",
    "               });",
    "  } else {",
    "    var subtable = $(id).DataTable({",
    "            'data': dataset,",
    "            'autoWidth': true,",
    "            'deferRender': true,",
    "            'info': false,",
    "            'lengthChange': false,",
    "            'ordering': d[n].length > 1,",
    "            'order': [],",
    "            'paging': true,",
    "            'scrollX': false,",
    "            'scrollY': false,",
    "            'searching': false,",
    "            'sortClasses': false,",
    "            'pageLength': 50,",
    "            'rowCallback': rowCallback,",
    "            'headerCallback': headerCallback,",
    "            'footerCallback': footerCallback,",
    "            'columnDefs': [",
    "              {targets: [0, 9, 10, 11], visible: false},",
    "              {targets: -1, visible: false},",
    "              {targets: 0, orderable: false, className: 'details-control'},",
    "              {targets: '_all', className: 'dt-center'}",
    "             ]",
    "          }).column(0).nodes().to$().css({cursor: 'pointer'});",
    "  }",
    "  subtable.MakeCellsEditable({",
    "    onUpdate: onUpdate,",
    "    inputCss: 'my-input-class',",
    "    columns: [2, 7, 8],",
    "    confirmationButton: {",
    "      confirmCss: 'my-confirm-class',",
    "      cancelCss: 'my-cancel-class'",
    "    }",
    "  });",
    "};",
    "",
    "// display the child table on click",
    "var children = [];", # array to store the id's of the already created child tables
    "table.on('click', 'td.details-control', function(){",
    "  var tbl = $(this).closest('table'),",
    "      tblId = tbl.attr('id'),",
    "      td = $(this),",
    "      row = $(tbl).DataTable().row(td.closest('tr')),",
    "      rowIdx = row.index();",
    "  if(row.child.isShown()){",
    "    row.child.hide();",
    "    td.html('<img src=\"https://raw.githubusercontent.com/DataTables/DataTables/master/examples/resources/details_open.png\"/>');",
    "  } else {",
    "    var childId = tblId + '-child-' + rowIdx;",
    "    if(children.indexOf(childId) === -1){", # this child table has not been created yet
    "      children.push(childId);",
    "      row.child(format(row.data(), childId)).show();",
    "    td.html('<img src=\"https://raw.githubusercontent.com/DataTables/DataTables/master/examples/resources/details_close.png\"/>');",
    "      format_datatable(row.data(), childId, rowIdx);",
    "    }else{",
    "      row.child(true);",
    "    td.html('<img src=\"https://raw.githubusercontent.com/DataTables/DataTables/master/examples/resources/details_close.png\"/>');",
    "    }",
    "  }",
    "});"
  )
  # Table
  table <- DT::datatable(
    Dat,
    callback = callback_js,
    rownames = rowNames,
    escape = -colIdx-1,
    style = "bootstrap4",
    extensions = 'Buttons',
      options = list(
        dom = "Blfrtip",
        buttons = list("copy",
                       list(
                          extend = "collection",
                          buttons = "csv",
                          text = "Download"
                          )
                      ),
        lengthMenu = list(c(-1, 10, 20),
                          c("All", 10, 20)),
        columnDefs = list(
          list(width = '30px', targets = 0),
          list(width = '545px', targets = 1),
          list(visible = FALSE, targets = ncol(Dat)-1+colIdx),
          list(orderable = FALSE, className = 'details-control', targets = colIdx),
          list(className = "dt-center", targets = "_all")
      )
    )
  )
  # Call the html tools deps (js & css files in this directory)
  cell_edit_dep <- htmltools::htmlDependency(
    "CellEdit", "1.0.19", 
    src = 'www/',
    script = "dataTables.cellEdit.js",
    stylesheet = "dataTables.cellEdit.css"
  )
  table$dependencies <- c(table$dependencies, list(cell_edit_dep))

  table %>% formatStyle(
    c('Market', 'SQAD CPP ($)', 'SQAD CPM ($)', 'Override CPP ($)', 'Override CPM ($)'),
    target = 'row',
    backgroundColor = "#F5F2F2"
  )
})
r shiny dt
1个回答
2
投票

试试这个。

js <- c(
  "function(xlsx) {",
  "  var table = $('#daypartTable').find('table').DataTable();",
  "  // Letters for Excel columns.",
  "  var LETTERS = [",
  "    'A','B','C','D','E','F','G','H','I','J','K','L','M',",
  "    'N','O','P','Q','R','S','T','U','V','W','X','Y','Z'",
  "  ];",
  "  // Get sheet.",
  "  var sheet = xlsx.xl.worksheets['sheet1.xml'];",
  "  // Get a clone of the sheet data.        ",
  "  var sheetData = $('sheetData', sheet).clone();",
  "  // Clear the current sheet data for appending rows.",
  "  $('sheetData', sheet).empty();",
  "  // Row count in Excel sheet.",
  "  var rowCount = 1;",
  "  // Iterate each row in the sheet data.",
  "  $(sheetData).children().each(function (index) {",
  "    // Used for DT row() API to get child data.",
  "    var rowIndex = index - 2;", #
  "    // Don't process row if its the header row.",
  sprintf("    if (index > 1 && index < %d) {", nrow(Dat)+2), #
  "      // Get row",
  "      var row = $(this.outerHTML);",
  "      // Set the Excel row attr to the current Excel row count.",
  "      row.attr('r', rowCount);",
  "      // Iterate each cell in the row to change the row number.",
  "      row.children().each(function (index) {",
  "        var cell = $(this);",
  "        // Set each cell's row value.",
  "        var rc = cell.attr('r');",
  "        rc = rc.replace(/\\d+$/, \"\") + rowCount;",
  "        cell.attr('r', rc);",
  "      });",
  "      // Get the row HTML and append to sheetData.",
  "      row = row[0].outerHTML;",
  "      $('sheetData', sheet).append(row);",
  "      rowCount++;",
  "      // Get the child data - could be any data attached to the row.",
  sprintf("      var childData = table.row(':eq(' + rowIndex + ')').data()[%d];", ncol(Dat)-1), #
  "      if (childData.length > 0) {",
  "        var colNames = Object.keys(childData[0]);",
  "        // Prepare Excel formatted row",
  "        headerRow = '<row r=\"' + rowCount +",
  "          '\"><c t=\"inlineStr\" r=\"A' + rowCount +",
  "          '\"><is><t></t></is></c>';",
  "        for(var i = 0; i < colNames.length; i++){",
  "          headerRow = headerRow +",
  "            '<c t=\"inlineStr\" r=\"' + LETTERS[i+1] + rowCount +",
  "            '\" s=\"7\"><is><t>' + colNames[i] +", 
  "            '</t></is></c>';",
  "        }",
  "        headerRow = headerRow + '</row>';",
  "        // Append header row to sheetData.",
  "        $('sheetData', sheet).append(headerRow);",
  "        rowCount++; // Inc excelt row counter.",
  "      }",
  "      // The child data is an array of rows",
  "      for (c = 0; c < childData.length; c++) {",
  "        // Get row data.",
  "        child = childData[c];",
  "        // Prepare Excel formatted row",
  "        var colNames = Object.keys(child);",
  "        childRow = '<row r=\"' + rowCount +",
  "          '\"><c t=\"inlineStr\" r=\"A' + rowCount +",
  "          '\"><is><t></t></is></c>';",
  "        for(var i = 0; i < colNames.length; i++){",
  "          childRow = childRow +",
  "            '<c t=\"inlineStr\" r=\"' + LETTERS[i+1] + rowCount +",
  "            '\" s=\"5\"><is><t>' + child[colNames[i]] +", 
  "            '</t></is></c>';",
  "        }",
  "        childRow = childRow + '</row>';",
  "        // Append row to sheetData.",
  "        $('sheetData', sheet).append(childRow);",
  "        rowCount++; // Inc excel row counter.",
  "      }",
  "      // Just append the header row and increment the excel row counter.",
  "    } else {",
  "      $('sheetData', sheet).append(this.outerHTML);",
  "      rowCount++;",
  "    }",
  "  });",
  "}"
)

datatable(
  Dat, callback = callback, rownames = rowNames, escape = -colIdx-1, 
  extensions = "Buttons", 
  options = list(
    dom = "Bfrtip",
    columnDefs = list(
      list(visible = FALSE, targets = ncol(Dat)-1+colIdx),
      list(orderable = FALSE, className = 'details-control', targets = colIdx),
      list(className = "dt-center", targets = "_all")
    ), 
    buttons = list(
      list(
        extend = "excel",
        exportOptions = list(
          orthogonal = "export", 
          columns = 0:(ncol(Dat)-2)
        ),
        orientation = "landscape",
        customize = JS(js)
      )
    )
  )
)

这是一个生成Excel文件的例子。

enter image description here


EDIT

更好的。

excelTitle <- NULL # set to NULL if you don't want a title
js <- c(
  "function(xlsx) {",
  "  var table = $('#daypartTable').find('table').DataTable();",
  "  // Number of columns.",
  "  var ncols = table.columns().count();",
  "  // Is there a title?",
  sprintf("  var title = %s;", ifelse(is.null(excelTitle), "false", "true")),
  "  // Integer to Excel column: 0 -> A, 1 -> B, ..., 25 -> Z, 26 -> AA, ...",
  "  var XLcolumn = function(j){", # https://codegolf.stackexchange.com/a/163919
  "    return j < 0 ? '' : XLcolumn(j/26-1) + String.fromCharCode(j % 26 + 65);",
  "  };",
  "  // Get sheet.",
  "  var sheet = xlsx.xl.worksheets['sheet1.xml'];",
  "  // Get a clone of the sheet data.        ",
  "  var sheetData = $('sheetData', sheet).clone();",
  "  // Clear the current sheet data for appending rows.",
  "  $('sheetData', sheet).empty();",
  "  // Row count in Excel sheet.",
  "  var rowCount = 1;",
  "  // Iterate each row in the sheet data.",
  "  $(sheetData).children().each(function (index) {",
  "    // Used for DT row() API to get child data.",
  "    var rowIndex = title ? index - 2 : index - 1;", 
  "    // Don't process row if it's the title row or the header row.",
  "    var i0 = title ? 1 : 0;",
  "    if (index > i0) {", 
  "      // Get row",
  "      var row = $(this.outerHTML);",
  "      // Set the Excel row attr to the current Excel row count.",
  "      row.attr('r', rowCount);",
  "      // Iterate each cell in the row to change the row number.",
  "      row.children().each(function (index) {",
  "        var cell = $(this);",
  "        // Set each cell's row value.",
  "        var rc = cell.attr('r');",
  "        rc = rc.replace(/\\d+$/, \"\") + rowCount;",
  "        cell.attr('r', rc);",
  "      });",
  "      // Get the row HTML and append to sheetData.",
  "      row = row[0].outerHTML;",
  "      $('sheetData', sheet).append(row);",
  "      rowCount++;",
  "      // Get the child data - could be any data attached to the row.",
  "      var childData = table.row(':eq(' + rowIndex + ')').data()[ncols-1];", 
  "      if (childData.length > 0) {",
  "        var colNames = Object.keys(childData[0]);",
  "        // Prepare Excel formatted row",
  "        var headerRow = '<row r=\"' + rowCount +",
  "          '\"><c t=\"inlineStr\" r=\"A' + rowCount +",
  "          '\"><is><t></t></is></c>';",
  "        for(let i = 0; i < colNames.length; i++){",
  "          headerRow = headerRow +",
  "            '<c t=\"inlineStr\" r=\"' + XLcolumn(i+1) + rowCount +",
  "            '\" s=\"7\"><is><t>' + colNames[i] +", 
  "            '</t></is></c>';",
  "        }",
  "        headerRow = headerRow + '</row>';",
  "        // Append header row to sheetData.",
  "        $('sheetData', sheet).append(headerRow);",
  "        rowCount++; // Inc excel row counter.",
  "      }",
  "      // The child data is an array of rows",
  "      for(let c = 0; c < childData.length; c++){",
  "        // Get row data.",
  "        var child = childData[c];",
  "        // Prepare Excel formatted row",
  "        var childRow = '<row r=\"' + rowCount +",
  "          '\"><c t=\"inlineStr\" r=\"A' + rowCount +",
  "          '\"><is><t></t></is></c>';",
  "        var i = 0;",
  "        for(let colname in child){",
  "          childRow = childRow +",
  "            '<c t=\"inlineStr\" r=\"' + XLcolumn(i+1) + rowCount +",
  "            '\" s=\"5\"><is><t>' + child[colname] +", 
  "            '</t></is></c>';",
  "          i++;",
  "        }",
  "        childRow = childRow + '</row>';",
  "        // Append row to sheetData.",
  "        $('sheetData', sheet).append(childRow);",
  "        rowCount++; // Inc excel row counter.",
  "      }",
  "      // Just append the header row and increment the excel row counter.",
  "    } else {",
  "      $('sheetData', sheet).append(this.outerHTML);",
  "      rowCount++;",
  "    }",
  "  });",
  "}"
)

datatable(
  Dat, callback = callback, rownames = rowNames, escape = -colIdx-1, 
  extensions = "Buttons", 
  options = list(
    dom = "Bfrtip",
    columnDefs = list(
      list(visible = FALSE, targets = ncol(Dat)-1+colIdx),
      list(orderable = FALSE, className = 'details-control', targets = colIdx),
      list(className = "dt-center", targets = "_all")
    ), 
    buttons = list(
      list(
        extend = "excel",
        exportOptions = list(
          orthogonal = "export", 
          columns = 0:(ncol(Dat)-2)
        ),
        title = excelTitle,
        orientation = "landscape",
        customize = JS(js)
      )
    )
  )
)

EDIT 2

callback_js替换

"  var dataset = [];",
"  var n = d.length - 1;",
"  for(var i = 0; i < d[n].length; i++){",
"    var datarow = $.map(d[n][i], function (value, index) {",
"      return [value];",
"    });",
"    dataset.push(datarow);",
"  }",
"  var id = 'table#' + childId;",
"  if (Object.keys(d[n][0]).indexOf('_details') === -1) {",
"    var subtable = $(id).DataTable({",
"                 'data': dataset,",
"                 'autoWidth': true,",
"                 'deferRender': true,",
"                 'info': false,",
"                 'lengthChange': false,",
"                 'ordering': d[n].length > 1,",
"                 'order': [],",
"                 'paging': true,",
"                 'scrollX': false,",
"                 'scrollY': false,",
"                 'searching': false,",
"                 'sortClasses': false,",
"                 'pageLength': 50,",
"                 'rowCallback': rowCallback,",
"                 'headerCallback': headerCallback,",
"                 'footerCallback': footerCallback,",
"                 'columnDefs': [",
"                  {targets: [0, 9, 10, 11], visible: false},",
"                  {targets: '_all', className: 'dt-center'}",
"                 ]",
"               });",
"  } else {",
"    var subtable = $(id).DataTable({",
"            'data': dataset,",
"            'autoWidth': true,",
"            'deferRender': true,",
"            'info': false,",
"            'lengthChange': false,",
"            'ordering': d[n].length > 1,",
"            'order': [],",
"            'paging': true,",
"            'scrollX': false,",
"            'scrollY': false,",
"            'searching': false,",
"            'sortClasses': false,",
"            'pageLength': 50,",
"            'rowCallback': rowCallback,",
"            'headerCallback': headerCallback,",
"            'footerCallback': footerCallback,",
"            'columnDefs': [",
"              {targets: [0, 9, 10, 11], visible: false},",
"              {targets: -1, visible: false},",
"              {targets: 0, orderable: false, className: 'details-control'},",
"              {targets: '_all', className: 'dt-center'}",
"             ]",
"          }).column(0).nodes().to$().css({cursor: 'pointer'});",
"  }",

"  var n = d.length - 1;",
"  var id = 'table#' + childId;",
"  var columns = Object.keys(d[n][0]).map(function(x){",
"    return {data: x, title: x};",
"  });",
"  if (Object.keys(d[n][0]).indexOf('_details') === -1) {",
"    var subtable = $(id).DataTable({",
"                 'data': d[n],",
"                 'columns': columns,",
"                 'autoWidth': true,",
"                 'deferRender': true,",
"                 'info': false,",
"                 'lengthChange': false,",
"                 'ordering': d[n].length > 1,",
"                 'order': [],",
"                 'paging': true,",
"                 'scrollX': false,",
"                 'scrollY': false,",
"                 'searching': false,",
"                 'sortClasses': false,",
"                 'pageLength': 50,",
"                 'rowCallback': rowCallback,",
"                 'headerCallback': headerCallback,",
"                 'footerCallback': footerCallback,",
"                 'columnDefs': [",
"                  {targets: [0, 9, 10, 11], visible: false},",
"                  {targets: '_all', className: 'dt-center'}",
"                 ]",
"               });",
"  } else {",
"    var subtable = $(id).DataTable({",
"            'data': d[n],",
"            'columns': columns,",
"            'autoWidth': true,",
"            'deferRender': true,",
"            'info': false,",
"            'lengthChange': false,",
"            'ordering': d[n].length > 1,",
"            'order': [],",
"            'paging': true,",
"            'scrollX': false,",
"            'scrollY': false,",
"            'searching': false,",
"            'sortClasses': false,",
"            'pageLength': 50,",
"            'rowCallback': rowCallback,",
"            'headerCallback': headerCallback,",
"            'footerCallback': footerCallback,",
"            'columnDefs': [",
"              {targets: [0, 9, 10, 11], visible: false},",
"              {targets: -1, visible: false},",
"              {targets: 0, orderable: false, className: 'details-control'},",
"              {targets: '_all', className: 'dt-center'}",
"             ]",
"          }).column(0).nodes().to$().css({cursor: 'pointer'});",
"  }",

此外,你可能不希望Excel文件中的隐藏列。所以将这段代码替换为

  "      if (childData.length > 0) {",
  "        var colNames = Object.keys(childData[0]);",
  "        // Prepare Excel formatted row",
  ......
  "        // Append row to sheetData.",
  "        $('sheetData', sheet).append(childRow);",
  "        rowCount++; // Inc excel row counter.",
  "      }",

改为

  "      if (childData.length > 0) {",
  "        var colNames = Object.keys(childData[0]).slice(1,9);",
  "        // Prepare Excel formatted row",
  "        var headerRow = '<row r=\"' + rowCount +",
  "          '\"><c t=\"inlineStr\" r=\"A' + rowCount +",
  "          '\"><is><t></t></is></c>';",
  "        for(let i = 0; i < colNames.length; i++){",
  "          headerRow = headerRow +",
  "            '<c t=\"inlineStr\" r=\"' + XLcolumn(i+1) + rowCount +",
  "            '\" s=\"7\"><is><t>' + colNames[i] +", 
  "            '</t></is></c>';",
  "        }",
  "        headerRow = headerRow + '</row>';",
  "        // Append header row to sheetData.",
  "        $('sheetData', sheet).append(headerRow);",
  "        rowCount++; // Inc excel row counter.",
  "      }",
  "      // The child data is an array of rows",
  "      for(let c = 0; c < childData.length; c++){",
  "        // Get row data.",
  "        var child = childData[c];",
  "        // Prepare Excel formatted row",
  "        var childRow = '<row r=\"' + rowCount +",
  "          '\"><c t=\"inlineStr\" r=\"A' + rowCount +",
  "          '\"><is><t></t></is></c>';",
  "        for(let i = 0; i < colNames.length; i++){",
  "          childRow = childRow +",
  "            '<c t=\"inlineStr\" r=\"' + XLcolumn(i+1) + rowCount +",
  "            '\" s=\"5\"><is><t>' + child[colNames[i]] +", 
  "            '</t></is></c>';",
  "        }",
  "        childRow = childRow + '</row>';",
  "        // Append row to sheetData.",
  "        $('sheetData', sheet).append(childRow);",
  "        rowCount++; // Inc excel row counter.",
  "      }",

EDIT 3

如果在子表的列名中有一些句号,这就不灵了。下面是修复方法。

  "  var columns = Object.keys(d[n][0]).map(function(x){",
  "    return {data: x.replace('.', '\\\\\\.'), title: x};",
  "  });",
© www.soinside.com 2019 - 2024. All rights reserved.