主子表联动实现文档

概述

主子表联动是一种常见的数据展示模式,点击主表(上表)的某一行时,下方的子表会自动加载并显示与该行相关的详细数据。

实现原理

1. 数据结构设计

主表数据结构(线路信息):

MainTableData: [
  {
    id: '1',
    routeName: '北京-上海',
    origin: '北京',
    destination: '上海',
    goodsName: '钢材',
    createdTime: '2025-11-18',
    // ...其他字段
  }
]

子表数据结构(价格信息):

SubTableData: [
  {
    id: '1',
    routeId: '1',  // 关联主表的ID
    price: 100,
    priceChange: '1',
    unit: '1',
    recordDate: '2025-11-18',
    // ...其他字段
  }
]

2. 核心实现步骤

步骤1:定义状态变量

data() {
  return {
    MainTableData: [],      // 主表数据
    SubTableData: [],       // 子表数据
    currentRow: null,       // 当前选中的主表行
  }
}

步骤2:主表添加行点击事件

<el-table 
  :data="MainTableData" 
  @row-click="rowClick"
  highlight-current-row
>
  <!-- 表格列定义 -->
</el-table>

关键属性说明

  • @row-click="rowClick" - 绑定行点击事件
  • highlight-current-row - 高亮当前选中行

步骤3:实现行点击处理方法

methods: {
  // 主表行点击事件
  rowClick(row) {
    // 1. 保存当前选中行
    this.currentRow = row
    
    // 2. 根据主表ID加载子表数据
    this.fetchSubData(row.id)
  },
  
  // 获取子表数据
  fetchSubData(routeId) {
    if (!routeId) {
      this.SubTableData = []
      return
    }
    
    const params = {
      routeId: routeId,  // 使用主表ID作为查询条件
    }

    getPricesList(params)
      .then(response => {
        if (response && response.code === '1000') {
          this.SubTableData = response.data || []
        }
      })
      .catch(error => {
        console.error('获取价格列表失败', error)
        this.$message.error('获取价格列表失败')
      })
  }
}

步骤4:子表展示

<el-row class="bg-white mt10">
  <!-- 显示当前选中的主表信息 -->
  <el-row>
    <el-col :span="24">
      <div class="cargo-header">
        <div class="cargo-source-no">
          线路名称 | {{ currentRow ? currentRow.routeName : '' }}
        </div>
      </div>
    </el-col>
  </el-row>
  
  <!-- 子表操作按钮 -->
  <el-row :gutter="10" class="mb8">
    <el-col :span="1.5">
      <el-button @click="handleSubAdd">新增</el-button>
    </el-col>
  </el-row>
  
  <!-- 子表数据展示 -->
  <el-row>
    <el-table :data="SubTableData" border>
      <!-- 子表列定义 -->
    </el-table>
  </el-row>
</el-row>

完整流程图

用户点击主表某一行
        ↓
触发 @row-click 事件
        ↓
调用 rowClick(row) 方法
        ↓
保存当前选中行到 currentRow
        ↓
调用 fetchSubData(row.id)
        ↓
发送API请求获取子表数据
        ↓
SubTableData 更新
        ↓
子表自动重新渲染显示新数据

关键技术点

1. 数据关联

  • 主表的 id 字段与子表的 routeId 字段建立关联关系
  • 通过主表ID作为查询参数获取对应的子表数据

2. 状态管理

// 保存当前选中行
this.currentRow = row

// 清空之前的子表数据
this.SubTableData = []

// 加载新的子表数据
this.fetchSubData(row.id)

3. 初始化处理

fetchData() {
  getRoutesList(params)
    .then(response => {
      // 重置选中状态
      this.currentRow = null
      this.SubTableData = []
      this.MainTableData = response.data.list || []
    })
}

当主表数据刷新时,清空选中状态和子表数据,避免显示脏数据。

4. 子表操作关联主表

// 子表新增时,自动关联当前选中的主表
handleSubAdd() {
  if (!this.currentRow) {
    this.$message.warning('请先选择一条线路记录')
    return
  }
  
  this.SubForm = {
    id: '',
    routeId: this.currentRow.id,  // 关联主表ID
    // ...其他字段
  }
  this.subDialogVisible = true
}

// 子表数据保存后,刷新当前主表行的子表数据
submitSubForm() {
  addPrice(formData)
    .then(response => {
      this.$message.success('新增成功')
      this.subDialogVisible = false
      // 刷新当前选中行的子表数据
      this.fetchSubData(this.currentRow.id)
    })
}

优化建议

1. 防止重复请求

rowClick(row) {
  // 如果点击的是同一行,不重复请求
  if (this.currentRow && this.currentRow.id === row.id) {
    return
  }
  
  this.currentRow = row
  this.fetchSubData(row.id)
}

2. 添加加载状态

data() {
  return {
    subTableLoading: false,  // 子表加载状态
  }
}

fetchSubData(routeId) {
  this.subTableLoading = true
  
  getPricesList(params)
    .then(response => {
      this.SubTableData = response.data || []
    })
    .finally(() => {
      this.subTableLoading = false
    })
}
<el-table :data="SubTableData" v-loading="subTableLoading">

3. 空状态提示

<el-empty 
  v-if="!currentRow" 
  description="请在上方选择一条线路记录"
/>

<el-table 
  v-else
  :data="SubTableData"
>

总结

主子表联动的核心是:

  1. 事件监听:通过 @row-click 监听主表行点击
  2. 状态管理:保存当前选中行到 currentRow
  3. 数据关联:使用主表ID查询对应的子表数据
  4. 响应式更新:子表数据更新后自动重新渲染

这种模式适用于任何具有主从关系的数据展示场景,如:订单-订单明细、部门-员工、分类-商品等。