Element ui 使用el-table 的expand-change异步加载数据 js

问题1:怎样第一次点击el-table的展开项就可以显示数据
问题2:怎样让子项各找各妈?(父项与子项的接口是两个接口,父项的id与子项的dictTypeId相匹配)

该回答引用ChatGPT

问题1:如何在第一次展开项时异步加载数据?

可以在 expand-change 事件中异步请求数据,然后将数据保存到子项中,再在 expanded 属性中设置子项展开时显示的数据。

例如:


<template>
  <el-table :data="tableData" @expand-change="handleExpandChange">
    <el-table-column prop="id" label="ID"></el-table-column>
    <el-table-column prop="name" label="Name"></el-table-column>
    <el-table-column type="expand">
      <template slot-scope="props">
        <div v-if="props.row.childrenLoading">Loading...</div>
        <ul v-else>
          <li v-for="child in props.row.children" :key="child.id">{{ child.name }}</li>
        </ul>
      </template>
    </el-table-column>
  </el-table>
</template>

<script>
export default {
  data() {
    return {
      tableData: []
    }
  },
  methods: {
    handleExpandChange(row, expanded) {
      if (expanded && row.children == null) {
        row.childrenLoading = true;
        // 异步请求子项数据
        axios.get(`/api/children?id=${row.id}`)
          .then(response => {
            row.children = response.data;
            row.childrenLoading = false;
          })
          .catch(error => {
            row.childrenLoading = false;
            console.error(error);
          });
      }
    }
  }
}
</script>


在 expand-change 事件中,首先判断是否展开项,然后判断子项是否已经存在,如果子项不存在,则设置 childrenLoading 属性为 true,表示正在加载数据,然后异步请求数据,将数据保存到子项中,最后将 childrenLoading 属性设置为 false。

在模板中,通过 v-if 和 v-else 判断展示子项的状态,如果正在加载数据,则显示 Loading...,否则显示子项数据。

问题2:如何让父项和子项分别请求不同的接口?

可以在 expand-change 事件中异步请求父项数据和子项数据,然后将数据保存到父项和子项中,最后在模板中分别展示父项和子项数据。

例如:

<template>
  <el-table :data="tableData" @expand-change="handleExpandChange">
    <el-table-column prop="id" label="ID"></el-table-column>
    <el-table-column prop="name" label="Name"></el-table-column>
    <el-table-column type="expand">
      <template slot-scope="props">
        <div v-if="props.row.childrenLoading">Loading...</div>
        <ul v-else>
          <li v-for="child in props.row.children" :key="child.id">{{ child.name }}</li>
        </ul>
      </template>
    </el-table-column>
  </el-table>
</template>

<script>
export default {
  data() {
    return {
      tableData: []
    }
  },
  methods: {
    handleExpandChange(row, expanded) {
      if (expanded && row.children == null) {
        row.childrenLoading = true;
        // 异步请求父项数据
        axios.get(`/api/parent?id=${row.id}`)
          .then(parentResponse => {
            row.parent = parentResponse.data;
            // 再次异步请求子项数据
            axios.get(`/api/children?dictTypeId=${row.parent.dictTypeId}`)
              .then(childrenResponse => {
                row.children = childrenResponse.data;
                row.childrenLoading = false;
              })
              .catch(error => {
                row.childrenLoading = false;
                console.error(error);
              });
          })
          .catch(error => {
            row.childrenLoading = false;
            console.error(error);
          });
      }
    }
  }
}
</script>



在父项数据请求的 then 回调函数中,首先将父项数据保存到 row.parent 属性中,然后再次异步请求子项数据,请求的参数为 dictTypeId=row.parent.dictTypeId,这样就能够获取与父项 id 相匹配的子项数据。最后将子项数据保存到 row.children 属性中。如果请求失败,则设置 childrenLoading 属性为 false,并打印错误日志。

可以用我提供的代码进行说明嘛?

<template>
  <div class="sys-container-box">
    <div class="committee-container">
      <div class="committee-head">
        <h3 v-page-title="$route"></h3>
        <div>
          <el-button type="primary" size="small" @click="handleDictionary('', 'add')">
            新增字典项
          </el-button>
        </div>
      </div>
      <div class="layout-search-wrap">
        <el-form ref="search" :model="searchForm" label-width="" size="small" inline>
          <el-form-item label="字典名称" prop="dictName">
            <el-input
              v-model="searchForm.dictName"
              @keyup.enter.native="searchData"
              placeholder="按 字典名称 检索"
            ></el-input>
          </el-form-item>
          <el-form-item label="字典类型" prop="dictType">
            <el-select
              v-model="searchForm.dictType"
              @keyup.enter.native="searchData"
              filterable
              placeholder="选择 字典类型"
            >
              <template v-for="item in typeData">
                <el-option
                  :key="item.dictTypeId"
                  :label="item.dictValue"
                  :value="item.id"
                ></el-option>
              </template>
            </el-select>
          </el-form-item>
          <el-form-item label="创建时间" prop="createTime">
            <el-date-picker
              v-model="searchForm.createTime"
              type="daterange"
              range-separator="至 "
              start-placeholder="开始日期"
              end-placeholder="结束日期"
              format="yyyy-MM-dd"
              value-format="yyyy-MM-dd"
              :picker-options="endDateOption"
            >
            </el-date-picker>
          </el-form-item>
          <el-form-item>
            <el-button type="primary" plain @click="searchData">查询</el-button>
            <el-button plain @click="resetSearch">重置</el-button>
          </el-form-item>
        </el-form>
      </div>
      <div class="layout-table-wrap">
        <div class="layout-table-body">
          <el-table
            @expand-change="expandChange"
            :expand-row-keys="expandKeys"
            row-key="id"
            :data="tableData"
            stripe
            :default-expand-all="expandAll"
            ref="table"
          >
            <el-table-column type="expand">
              <template slot-scope="">
                <el-table
                  v-loading="!this.isShow"
                  size="mini"
                  row-key="id"
                  :header-cell-style="{
                    background: '#ebf4fe',
                    height: '50px'
                  }"
                  :row-style="{ background: '#ebf4fe' }"
                  class="table-style"
                  :data="tags"
                  style="width: 60%;font-size: 10px;margin-left :auto"
                >
                  <template v-for="column in childTableColumn">
                    <el-table-column
                      :prop="column.prop"
                      :key="column.prop"
                      :label="column.label"
                      :width="column.width || ''"
                      :align="column.align || 'center'"
                      min-width="90"
                    >
                    </el-table-column>
                  </template>
                  <el-table-column fixed="right" label="操作" width="250" align="center">
                    <template slot-scope="scope">
                      <el-button
                        type="success"
                        plain
                        size="mini"
                        @click="handleTree(scope.row, 'edit')"
                      >
                        编辑
                      </el-button>
                      <el-button
                        type="danger"
                        plain
                        size="mini"
                        @click="delChildDictionary(scope.row)"
                      >
                        删除
                      </el-button>
                    </template>
                  </el-table-column>
                </el-table>
              </template>
            </el-table-column>
            <template v-for="column in tableColumn">
              <el-table-column
                :prop="column.prop"
                :key="column.prop"
                :label="column.label"
                :width="column.width || ''"
                :align="column.align || 'center'"
                min-width="110"
              >
                <template slot-scope="scope">
                  <template v-if="column.prop === 'sort'">
                    <el-tag size="mini">
                      {{ scope.row.sort }}
                    </el-tag>
                  </template>
                  <template v-else>{{ scope.row[column.prop] || '-' }}</template>
                </template>
              </el-table-column>
            </template>
            <el-table-column fixed="right" label="操作" width="250" align="center">
              <template slot-scope="scope">
                <el-button
                  type="success"
                  plain
                  size="mini"
                  @click="handleDictionary(scope.row, 'edit')"
                >
                  编辑
                </el-button>
                <el-button type="primary" plain size="mini" @click="handleTree(scope.row, 'add')"
                  >添加子项</el-button
                >
                <el-button type="danger" plain size="mini" @click="delDictionary(scope.row)">
                  删除
                </el-button>
              </template>
            </el-table-column>
          </el-table>
        </div>
        <div class="layout-table-foot">
          <pagination
            :page="searchForm.pageNo"
            :size="searchForm.pageSize"
            :total="tableTotal"
            @page-change="pageChange"
            @size-change="sizeChange"
          ></pagination>
        </div>
      </div>
    </div>
    <form-dialog
      :visible="visible"
      :id="rowId"
      :type="type"
      @close="dialogClose"
      @submit="dialogSubmit"
    />
    <tree-dialog
      :visible="visibleTree"
      :id="TreeId"
      :type="typeTree"
      @close="dialogClose"
      @submit="dialogTreeSubmit"
    />
  </div>
</template>

<script>
import {
  dictionaryManagementSearch_committee,
  dictionaryManagementTable_committee,
  childDictionaryManagementTable_committee
} from '_committee/config/table-form.js'
import {
  dictDataComboBoxCommittee,
  listDictDataComboBoxCommittee,
  delDictDataComboBoxCommittee,
  delChildDictDataComboBoxCommittee,
  listChildDictDataComboBoxCommittee
} from '_committee/config/api'
import { elConfirm } from '@/utils/elComponent.js'
import { isShowBtn } from '_committee/config/utils.js'

export default {
  name: 'CommitteeBusinessOperation',
  components: {
    Pagination: () => import('@/components/pagination/index.vue'),
    FormDialog: () => import('./form-dialog.vue'),
    TreeDialog: () => import('./tree-dialog.vue')
  },
  data() {
    return {
      tableData: [],
      tags: [],
      isShow: false,
      expandKeys: [],
      dictTypeId: '',
      typeData: [],
      tableTotal: 0,
      searchForm: dictionaryManagementSearch_committee(),
      tableColumn: dictionaryManagementTable_committee,
      childTableColumn: childDictionaryManagementTable_committee,
      visible: false,
      visibleTree: false,
      type: '',
      typeTree: '',
      rowId: '',
      TreeId: '',
      endDateOption: {
        disabledDate: time => {
          return time.getTime() > new Date().getTime()
        }
      }
    }
  },
  computed: {
    expandAll() {
      return this.from === 'moudle'
    },
    userInfo() {
      return this.$store.getters.getActiveRole
    }
  },
  mounted() {
    this.loadData()
  },
  methods: {
    setShowBtn(row, type = 'edit') {
      return isShowBtn(this.userInfo, row, type)
    },
    loadTypeData() {
      // 加载类型下拉
      dictDataComboBoxCommittee()
        .then(res => {
          this.pageLoading = false
          if (res.status === '0000') {
            this.typeData = res.data
          }
        })
        .catch(() => {
          this.pageLoading = false
        })
    },
    //  获取表格数据
    loadData() {
      let options = this.searchForm
      options.companyOrgId = this.userInfo.companyOrgId
      options.siteOrgId = this.userInfo.siteOrgId
      listDictDataComboBoxCommittee(options)
        .then(res => {
          this.pageLoading = false
          if (res.status === '0000') {
            this.tableData = res.data.records
            this.tableTotal = res.data.total
            this.dictTypeId = res.data.records
          }
          this.loadTypeData()
        })
        .catch(() => {})
    },
    // 加载子项列表
    loadTreeData() {
      let options = {}
      options.companyOrgId = this.userInfo.companyOrgId
      options.siteOrgId = this.userInfo.siteOrgId
      options.dictTypeId = this.TreeId
      listChildDictDataComboBoxCommittee(options)
        .then(res => {
          this.pageLoading = false
          if (res.status === '0000') {
            this.tags = res.data.records
            console.log(this.tags)
          }
        })
        .catch(() => {})
    },
    // 展开行
    expandChange(row) {
      if (!this.isShow) {
        //根据判定是否已经加载了数据
        setTimeout(() => {
          let options = {}
          options.companyOrgId = this.userInfo.companyOrgId
          options.siteOrgId = this.userInfo.siteOrgId
          options.dictTypeId = this.TreeId
          listChildDictDataComboBoxCommittee(options)
            .then(res => {
              this.pageLoading = false
              if (res.status === '0000') {
                let dateList = res.data.records.map(function(item) {
                  return item['dictTypeId']
                })

                dateList = dateList.filter(val => val == row.id)
                console.log(row.id)
                console.log(dateList)
                this.tags = res.data.records
                console.log(this.tags)
                this.isShow = true //加载成功之后更新标识

                this.expands.push(row.id)
                console.log(this.expands)
                console.log(this.isShow)
              }
            })
            .catch(() => {})

          this.$message({
            showClose: true,
            message: '数据加载成功'
          })
        }, 1000)
      }
      // 拿id
      // console.log(row.id)
      // this.TreeId = row.id
      // console.log(this.tags)
      // console.log(expandedRows[0].id)
      // let that = this
      // that.$nextTick(function() {
      //   this.loadTreeData()
      // })
      // this.loadTreeData()
      // if (expandedRows.length == 1) {
      //   this.expandKeys.shift()
      //   this.expandKeys = []
      //   return
      // }
      // console.log(expandedRows)
      // console.log(this.TreeId)
      // if (this.expands.includes(row.id)) {
      //   this.expands = this.expands.filter(val => val !== row.id)
      // } else {
      //   this.expands.push(row.id)
      // }
      // console.log(expandedRows)
      // console.log(this.tags)
      // console.log(this.expandKeys)
      // if (!expandedRows.length) {
      //   this.expandKeys.shift()
      //   this.expandKeys = []
      //   return
      // }
      // // //将展开行的id push到expandKeys数组里面
      // this.expandKeys = []
      // expandedRows.forEach(item => {
      //   this.expandKeys.shift()
      //   this.expandKeys.push(item.id)
      // })
      // // //判断是否关闭当前行
      // if (expandedRows.indexOf(row) == -1) {
      //   this.expandKeys.shift()
      //   row.expandedRows = []
      //   return
      // }
      // this.loadTreeData()
    },
    //  检索数据
    searchData() {
      this.searchForm.pageNo = 1
      this.loadData()
    },
    //  重置搜索表单
    resetSearch() {
      this.searchForm = dictionaryManagementSearch_committee()
      this.$nextTick(() => this.searchData())
    },
    // 新增/编辑
    handleDictionary(row, type) {
      this.visible = true
      this.type = type
      if (row) {
        this.rowId = row.id
      }
    },
    // 子项新增/编辑
    handleTree(row, typeTree) {
      this.visibleTree = true
      this.typeTree = typeTree
      if (row) {
        this.TreeId = row.id
      }
    },
    //  删除
    delDictionary(row) {
      console.log(row)
      elConfirm({
        title: '删除确认',
        content: `确定删除 ${row.dictName} 吗?`,
        callback: res => {
          if (res === 'success') {
            const options = {
              id: row.id
            }
            delDictDataComboBoxCommittee(options).then(res => {
              if (res.status === '0000') {
                this.$message.success('删除成功')
              } else {
                this.$message.error(`${res.msg} (${res.status})`)
              }
              this.loadData()
            })
          } else {
            this.$message.error('取消删除')
          }
        }
      })
    },
    // 删除子项
    delChildDictionary(row) {
      console.log(row)
      elConfirm({
        title: '删除确认',
        content: `确定删除 ${row.id} 吗?`,
        callback: res => {
          if (res === 'success') {
            const options = {
              id: row.id
            }
            delChildDictDataComboBoxCommittee(options).then(res => {
              if (res.status === '0000') {
                this.$message.success('删除成功')
              } else {
                this.$message.error(`${res.msg} (${res.status})`)
              }
              this.loadTreeData()
            })
          } else {
            this.$message.error('取消删除')
          }
        }
      })
    },
    //  切换分页显示条数
    sizeChange(size) {
      this.searchForm.pageNo = 1
      this.searchForm.pageSize = size
      this.loadData()
    },
    //  翻页
    pageChange(page) {
      this.searchForm.pageNo = page
      this.loadData()
    },
    dialogClose() {
      this.visible = false
      this.visibleTree = false
    },
    dialogSubmit() {
      this.visible = false
      this.loadData()
    },
    dialogTreeSubmit() {
      this.visibleTree = false
      this.loadTreeData()
    }
  }
}
</script>

<style scoped lang="scss">
el-table ::v-deep .el-table tr {
  background-color: yellow;
}
</style>