table实现左右水平滚动

  <template>
    <div class="fixed-table-container">
      <!-- 顶部固定表头 -->
      <div class="top-header">
        <!-- 左侧表头滚动区 -->
        <div class="top-left-scroll" ref="topLeftScroll">
          <table>
            <thead>
              <tr>
                <th v-for="(item, index) in leftHeaders" :key="'t-l-' + index">
                  {{ item }}
                </th>
              </tr>
            </thead>
          </table>
        </div>
        
        <!-- 中间固定轴表头 -->
        <div class="top-middle-axis">
          <table>
            <thead>
              <tr>
                <th>{{ middleAxisText }}</th>
              </tr>
            </thead>
          </table>
        </div>
        
        <!-- 右侧表头滚动区 -->
        <div class="top-right-scroll" ref="topRightScroll">
          <table>
            <thead>
              <tr>
                <th v-for="(item, index) in rightHeaders" :key="'t-r-' + index">
                  {{ item }}
                </th>
              </tr>
            </thead>
          </table>
        </div>
      </div>

      <!-- 垂直滚动容器 -->
      <div class="vertical-scroll-container">
        <!-- 中间内容区域 -->
        <div class="content-container">
          <!-- 左侧内容滚动区 -->
          <div class="content-left-scroll"
              ref="contentLeftScroll"
              @scroll="syncLeftHorizontalScroll">
            <table>
              <tbody>
                <tr v-for="(row, rowIdx) in tableData" :key="rowIdx">
                  <td v-for="(val, colIdx) in row.left" :key="'l-' + rowIdx + '-' + colIdx">
                    <input 
                      type="text" 
                      v-model="tableData[rowIdx].left[colIdx]" 
                      class="content-input" />
                  </td>
                </tr>
              </tbody>
            </table>
          </div>
          
          <!-- 中间固定轴 -->
          <div class="content-middle-axis">
            <table>
              <tbody>
                <tr v-for="(row, rowIdx) in tableData" :key="rowIdx">
                  <td>{{ row.middle }}</td>
                </tr>
              </tbody>
            </table>
          </div>
          
          <!-- 右侧内容滚动区 -->
          <div class="content-right-scroll"
              ref="contentRightScroll"
              @scroll="syncRightHorizontalScroll">
            <table>
              <tbody>
                <tr v-for="(row, rowIdx) in tableData" :key="rowIdx">
                  <td v-for="(val, colIdx) in row.right" :key="'r-' + rowIdx + '-' + colIdx">
                    <input 
                      type="text" 
                      v-model="tableData[rowIdx].right[colIdx]" 
                      class="content-input" />
                  </td>
                </tr>
              </tbody>
            </table>
          </div>
        </div>
      </div>
    </div>
  </template>

  <script>
    export default {
      name: "AlignedBorderTable",
      data() {
        const columnCount = 10; // 每侧列数
        const rowCount = 50; // 行数
      
        // 生成顶部左侧表头
        const leftHeaders = Array.from({ length: columnCount }, (_, i) => 
          ((i + 1) * 0.25).toFixed(2)
        ).reverse();
      
        // 生成顶部右侧表头
        const rightHeaders = Array.from({ length: columnCount }, (_, i) => 
          ((i + 1) * 0.25).toFixed(2)
        );
      
        // 生成表格内容数据(序号格式)
        const tableData = Array.from({ length: rowCount }, (_, rowIdx) => ({
          left: Array.from({ length: columnCount }, (_, colIdx) => `L${rowIdx}_${colIdx}`),
          middle: (rowIdx * 0.25).toFixed(2),
          right: Array.from({ length: columnCount }, (_, colIdx) => `R${rowIdx}_${colIdx}`)
        }));

        return {
          middleAxisText: "+/-",
          leftHeaders,
          rightHeaders,
          tableData,
          isSyncing: false
        };
      },
      mounted () {
        console.log("333")
        console.log(this.leftHeaders)
        console.log(this.rightHeaders)
        console.log(this.tableData)
      } ,
      methods: {
        // 同步左侧水平滚动
        syncLeftHorizontalScroll(e) {
          if (this.isSyncing) {return;}
          this.isSyncing = true;
        
          const scrollLeft = e.target.scrollLeft;
          if (this.$refs.topLeftScroll) {
            this.$refs.topLeftScroll.scrollLeft = scrollLeft;
          }
        
          this.isSyncing = false;
        },
      
        // 同步右侧水平滚动
        syncRightHorizontalScroll(e) {
          if (this.isSyncing) {return;}
          this.isSyncing = true;
        
          const scrollLeft = e.target.scrollLeft;
          if (this.$refs.topRightScroll) {
            this.$refs.topRightScroll.scrollLeft = scrollLeft;
          }
        
          this.isSyncing = false;
        }
      }
    };
  </script>

  <style scoped>
  .fixed-table-container {
    width: 100%;
    max-width: 1200px;
    height: 500px;
    border: 1px solid #333;
    overflow: hidden;
    font-family: Arial, sans-serif;
    /* 核心:使用border-box确保边框包含在尺寸内 */
    box-sizing: border-box;
  }

  /* 顶部固定表头 */
  .top-header {
    display: flex;
    height: 40px;
    background-color: #ffd700;
    border-bottom: 1px solid #333;
    position: relative;
    z-index: 2;
  }

  /* 顶部左侧滚动区 */
  .top-left-scroll {
    flex: 1;
    height: 100%;
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: none;
    -ms-overflow-style: none;
  }

  .top-left-scroll::-webkit-scrollbar {
    display: none;
  }

  /* 顶部右侧滚动区 */
  .top-right-scroll {
    flex: 1;
    height: 100%;
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: none;
    -ms-overflow-style: none;
  }

  .top-right-scroll::-webkit-scrollbar {
    display: none;
  }

  /* 顶部中间固定轴 */
  .top-middle-axis {
    width: 80px;
    height: 100%;
    background-color: #ffc107;
    border-left: 1px solid #333;
    border-right: 1px solid #333;
    flex-shrink: 0;
  }

  /* 表头表格样式 - 关键对齐设置 */
  .top-header table {
    width: auto;
    border-collapse: collapse;
    border-spacing: 0;
    margin: 0;
    padding: 0;
  }

  .top-header th {
    width: 80px; /* 固定宽度,与内容区单元格一致 */
    min-width: 80px;
    height: 40px; /* 固定高度,与内容区行高一致 */
    border-right: 1px solid #333;
    text-align: center;
    padding: 0; /* 移除内边距避免宽度偏差 */
    font-weight: normal;
    margin: 0;
  }

  /* 最后一列去除右边框,避免与中间轴边框重叠 */
  .top-left-scroll th:last-child {
    border-right: none;
  }

  .top-right-scroll th:last-child {
    border-right: none;
  }

  /* 垂直滚动容器 */
  .vertical-scroll-container {
    height: calc(100% - 40px);
    overflow-y: auto;
  }

  /* 内容区域容器 */
  .content-container {
    display: flex;
    min-height: 100%;
  }

  /* 左侧内容滚动区 */
  .content-left-scroll {
    flex: 1;
    overflow-x: auto;
    overflow-y: hidden;
  }

  /* 中间固定轴内容区 */
  .content-middle-axis {
    width: 80px;
    background-color: #ffc107;
    border-right: 1px solid #333;
    flex-shrink: 0;
  }

  /* 右侧内容滚动区 */
  .content-right-scroll {
    flex: 1;
    overflow-x: auto;
    overflow-y: hidden;
  }

  /* 内容区表格样式 - 关键对齐设置 */
  .content-left-scroll table,
  .content-middle-axis table,
  .content-right-scroll table {
    width: auto;
    border-collapse: collapse;
    border-spacing: 0;
    margin: 0;
    padding: 0;
  }

  .content-left-scroll td,
  .content-middle-axis td,
  .content-right-scroll td {
    width: 80px; /* 固定宽度,与表头一致 */
    min-width: 80px;
    height: 40px; /* 固定高度,与表头一致 */
    border-right: 1px solid #333;
    border-bottom: 1px solid #333;
    text-align: center;
    padding: 0; /* 移除内边距避免宽度偏差 */
    margin: 0;
  }

  /* 最后一列去除右边框 */
  .content-left-scroll td:last-child {
    border-right: none;
  }

  .content-right-scroll td:last-child {
    border-right: none;
  }

  .content-middle-axis td {
    border-right: none;
    font-weight: bold;
    line-height: 40px; /* 垂直居中 */
  }

  /* 内容区输入框样式 */
  .content-input {
    width: 100%;
    height: 100%;
    padding: 0 8px;
    border: none; /* 去除输入框自身边框 */
    box-sizing: border-box;
    text-align: center;
    background-color: #fff;
    font-size: 14px;
  }

  .content-input:focus {
    outline: none;
    background-color: #f0f7ff;
  }

  /* 修复第一行顶部边框与表头底部边框对齐 */
  .content-left-scroll tr:first-child td,
  .content-middle-axis tr:first-child td,
  .content-right-scroll tr:first-child td {
    border-top: none; /* 移除第一行顶部边框,避免与表头底部边框重叠 */
  }
  </style>
posted @ 2025-11-11 10:15  不完美的完美  阅读(3)  评论(0)    收藏  举报