SkyBlog

计算 h5 表格每列宽度

这篇文章发布于 2024年10月12日,星期六,01:37,归类于 算法阅读 ? 次,0 条评论

从 wps 复制文档,获取到如下 html 表格, 计算表格每列的宽度后,插入 colgroup>col[width="61"] 标签用于控制表格每列宽度。

Snipaste_2024-10-12_14-32-02.png
Snipaste_2024-10-12_14-32-02.png
<table>
  <tbody>
    <tr>
      <td width="61" colspan="1" rowspan="1"></td>
      <td width="723" colspan="7" rowspan="1"></td>
    </tr>
    <tr>
      <td width="61" colspan="1" rowspan="1"></td>
      <td width="723" colspan="7" rowspan="1"></td>
    </tr>
    <tr>
      <td width="61" colspan="1" rowspan="1"></td>
      <td width="723" colspan="7" rowspan="1"></td>
    </tr>
    <tr>
      <td width="61" colspan="1" rowspan="2"></td>
      <td width="91" colspan="1" rowspan="1"></td>
      <td width="106" colspan="1" rowspan="1"></td>
      <td width="47" colspan="1" rowspan="1"></td>
      <td width="84" colspan="1" rowspan="1"></td>
      <td width="394" colspan="3" rowspan="1"></td>
    </tr>
    <tr>
      <td width="91" colspan="1" rowspan="1"></td>
      <td width="106" colspan="1" rowspan="1"></td>
      <td width="47" colspan="1" rowspan="1"></td>
      <td width="84" colspan="1" rowspan="1"></td>
      <td width="394" colspan="3" rowspan="1"></td>
    </tr>
    <tr>
      <td width="61" colspan="1" rowspan="2"></td>
      <td width="723" colspan="7" rowspan="1"></td>
    </tr>
    <tr>
      <td width="723" colspan="7" rowspan="1"></td>
    </tr>
    <tr>
      <td width="61" colspan="1" rowspan="1"></td>
      <td width="337" colspan="5" rowspan="1"></td>
      <td width="65" colspan="1" rowspan="1"></td>
      <td width="320" colspan="1" rowspan="1"></td>
    </tr>
  </tbody>
</table>

转化为下面的数据结构

type TdAttr = { width: number; colspan: number; rowspan: number }
 
const tdAttr: TdAttr[][] = [
  [
    { width: 61, colspan: 1, rowspan: 1 },
    { width: 723, colspan: 7, rowspan: 1 }
  ],
  [
    { width: 61, colspan: 1, rowspan: 1 },
    { width: 723, colspan: 7, rowspan: 1 }
  ],
  [
    { width: 61, colspan: 1, rowspan: 1 },
    { width: 723, colspan: 7, rowspan: 1 }
  ],
  [
    { width: 61, colspan: 1, rowspan: 2 },
    { width: 91, colspan: 1, rowspan: 1 },
    { width: 106, colspan: 1, rowspan: 1 },
    { width: 47, colspan: 1, rowspan: 1 },
    { width: 84, colspan: 1, rowspan: 1 },
    { width: 394, colspan: 3, rowspan: 1 }
  ],
  [
    { width: 91, colspan: 1, rowspan: 1 },
    { width: 106, colspan: 1, rowspan: 1 },
    { width: 47, colspan: 1, rowspan: 1 },
    { width: 84, colspan: 1, rowspan: 1 },
    { width: 394, colspan: 3, rowspan: 1 }
  ],
  [
    { width: 61, colspan: 1, rowspan: 2 },
    { width: 723, colspan: 7, rowspan: 1 }
  ],
  [
    { width: 723, colspan: 7, rowspan: 1 }
  ],
  [
    { width: 61, colspan: 1, rowspan: 1 },
    { width: 337, colspan: 5, rowspan: 1 },
    { width: 65, colspan: 1, rowspan: 1 },
    { width: 320, colspan: 1, rowspan: 1 }
  ]
]

算法如下

function calculateColumnWidths(tds: TdAttr[][]) {
  // 列数
  const columnCount = tds[0].reduce((acc, cell) => acc + cell.colspan, 0)
  // 定义 dp[i][j] 表示 i 到 j 列的宽度,初始值为 0。(从 0 开始计数列数)
  const dp = Array.from({ length: columnCount }, () => new Array<number>(columnCount).fill(0))
 
  // 用于记录每列被跨行占据的行数
  const occupied = new Array(columnCount).fill(0)
 
  // 填充 dp
  for (const row of tds) {
    let currentColumnIndex = 0
    const minRowspan = Math.min(...row.map(it => it.rowspan))
    for (const cell of row) {
      while (currentColumnIndex < columnCount && occupied[currentColumnIndex] > 0) {
        occupied[currentColumnIndex] -= minRowspan
        currentColumnIndex++
      }
      if (cell.rowspan > minRowspan) {
        for (let i = 0; i < cell.colspan; i++) {
          occupied[currentColumnIndex + i] = cell.rowspan - minRowspan
        }
      }
      if (currentColumnIndex + cell.colspan - 1 >= columnCount) break
      dp[currentColumnIndex][currentColumnIndex + cell.colspan - 1] = cell.width
      currentColumnIndex += cell.colspan
    }
  }
 
  // 计算 dp
  for (let k = 0; k < (columnCount * columnCount) / 2; k++) {
    for (let len = 1; len < columnCount; len++) {
      for (let i = 0; i + len < columnCount; i++) {
        let j = i + len
        if (dp[i][j] == 0 && dp[i][j - 1] != 0 && dp[j][j] != 0) {
          dp[i][j] = dp[i][j - 1] + dp[j][j]
        }
        if (dp[i][j - 1] == 0 && dp[i][j] != 0 && dp[j][j] != 0) {
          dp[i][j - 1] = dp[i][j] - dp[j][j]
        }
        if (dp[j][j] == 0 && dp[i][j] != 0 && dp[i][j - 1] != 0) {
          dp[j][j] = dp[i][j] - dp[i][j - 1]
        }
      }
    }
  }
 
  return dp.map((it, index) => it[index])
}