主要方法:https://blog.csdn.net/m0_57391092/article/details/119191630

第一天:

父组件:index.vue
搜索组件:SearchBar.vue
表格组件:TableList.vue
表格组件里的数据通过:

  1. defineExpose({
  2. showCols,
  3. })

暴露出去。

  1. const showCols = computed(() => {
  2. return {
  3. productShowCols: toRaw(TabMapDataSource[TabsEnum.REGISTER].showColumns), //注册信息
  4. batchShowCols: toRaw(TabMapDataSource[TabsEnum.BATCH].showColumns) // 批次信息
  5. }
  6. })

image.png
image.png
image.pngimage.png
父组件中通过:

  1. <TableList
  2. ref="tableListRef" //总的ref
  3. />
  4. const tableListRef = ref()

来接收总的ref。
父组件中通过tableListRef.value.showCols拿到数据:

  1. /**
  2. * 导出excel
  3. */
  4. const exportExcel = (callback) => {
  5. // callback()
  6. console.log(tableListRef.value.showCols, tableListData) //表头和数据
  7. }
  1. //表单总数
  2. const tableListData = reactive({
  3. [TabsEnum.REGISTER]: {}, //注册表格信息
  4. [TabsEnum.BATCH]: {} //批次表格信息
  5. })

searchbar组件中通过引入导出excel所需的包:

  1. import ExportJsonExcel from 'js-export-excel'
  2. // 导出按钮,通过调用方法发出自定义事件
  3. <div class="search-right" @click="exportExcel"><a-button>导出</a-button></div>
  4. // 发出自定义事件,将封装的方法给父组件
  5. const exportExcel = () => {
  6. Emit('exportExcel', exportFunc)
  7. }

父组件通过exportExcel自定义事件来触发方法,他接收了封装的方法作为参数:

  1. const exportExcel = (callback) => {
  2. // callback()
  3. }

image.png
image.png

第二天:

企业微信截图_16565528121495.png
可以选择字段导出:
企业微信截图_16565550039629.png
父组件拿到searchBar子组件发送的自定义事件,接收了mode(选择当前还是全部字段)和导出excel的回调函数作为参数;通过selectCols添加注册信息和批次信息的数组,通过switch语句选择是当前字段还是全部字段,把注册信息和批次信息数组处理成向后端发送的格式,然后拼接到condition里,通过exportInfo调用接口拿到返回数据,解构出data,放到回调函数里执行。

  1. <template>
  2. <SearchBar @exportExcel="exportExcel" @handleSearch="handleSearch" :filter="filter" :frameFilters="frameFilters" />
  3. <TableList
  4. ref="tableListRef"
  5. :loading="refreshTable.loading"
  6. @changeTab="changeTab"
  7. @changePagination="changePagination"
  8. :currentTab="currentTab"
  9. :tableListData="tableListData"
  10. />
  11. </template>
  12. <script setup lang="ts">
  13. import SearchBar from './components/SearchBar.vue'
  14. import TableList from './components/TableList.vue'
  15. /**
  16. * tab 相关
  17. */
  18. const currentTab = reactive({ value: TabsEnum.REGISTER }) //当前tab是注册还是批次
  19. // 导入请求接口
  20. import {
  21. getExportColumnsData,
  22. getExportProd
  23. } from '@/api/material'
  24. // 导入ts类型
  25. import {
  26. // 当前字段还是全部字段
  27. exportExcelModeEnum
  28. } from '@/views/register/registration-and-batch-info-query/types'
  29. const tableListRef = ref() // 拿到TableList组件导出的内容
  30. const globalSearchInfo = ref(initSearchInfo())
  31. /**
  32. * 导出接口
  33. */
  34. const exportInfo = {
  35. // 注册的接口
  36. [TabsEnum.REGISTER]: {
  37. method: getExportProd,
  38. prefixName: t('register.registrationAndBatchInfoQuery.queryViewDetail.product')
  39. },
  40. // 批次的接口
  41. [TabsEnum.BATCH]: {
  42. method: getExportColumnsData,
  43. prefixName: t('register.registrationAndBatchInfoQuery.queryViewDetail.batch')
  44. }
  45. }
  46. /**
  47. * 导出
  48. */
  49. const exportExcel = async (mode, callback: Function) => {
  50. let condition = {}
  51. let selectCols = {
  52. productShowCols: [],
  53. batchShowCols: []
  54. }
  55. // 判断是当前字段还是所有字段
  56. switch (mode) {
  57. // 如果是当前字段
  58. case exportExcelModeEnum.CURRENT_SHOW: {
  59. // 拿到注册和批次表格头
  60. const { productShowCols, batchShowCols } = tableListRef.value.showCols
  61. // 过滤一下数组然后放到selectCols里面
  62. seletCols.productShowCols = deBuildColumns(productShowCols)
  63. seletCols.batchShowCols = deBuildColumns(batchShowCols)
  64. // 拼接对象
  65. condition = Object.assign({}, globalSearchInfo.value, seletCols)
  66. break
  67. }
  68. // 如果是全部字段
  69. default: {
  70. condition = Object.assign({}, globalSearchInfo.value, tableListRef.value.currentAllCols)
  71. break
  72. }
  73. }
  74. // 调用接口
  75. const { data } = await exportInfo[currentTab.value].method(condition)
  76. // 调用回调
  77. callback(data, { fileName: `${exportInfo[currentTab.value].prefixName}_${treeNodeData.value.name}` })
  78. }
  79. </script>
// 注册信息还是批次信息
export enum TabsEnum {
  REGISTER = 1,  // 注册
  BATCH   //批次
}
// 当前字段还是全部字段
export enum exportExcelModeEnum {
  ALL,
  CURRENT_SHOW
}
export function initSearchInfo(): SearchInfo {
  return {
    keyword: '',
    sortColumn: '',
    pageSize: 10,
    pageNum: 1,
    filter: {
      frameId: '',
      frameName: '',
      productName: '',
      batchName: '',
      batchParams: [],
      productCustomParams: [],
      batchCustomParams: []
    },
    productShowCols: [],
    batchShowCols: []
  }
}
export const deBuildColumns = (columns) => {
  columns = columns || []
  return columns.map((column) => ({
    name: column.title,
    key: column.dataIndex,
    type: column.type
  }))
}

用户点击按钮触发下拉菜单,触发handleSelectExportExcelMode方法,选择导出全部字段还是当前字段(exportExcelModeEnum),在方法中触发exportExcel(exportExcelMode.value)的调用;exportExcel方法中发送了一个exportExcel自定义事件给父组件,将exportExcelMode,exportFunc作为参数传递。

<template>
  <div class="search-right">
    <!--用户选择时触发handleSelectExportExcelMode方法-->
    <a-dropdown @select="handleSelectExportExcelMode">
      <a-button>导出</a-button>
      <template #content>
        <!--value是选项值-->
        <a-doption :value="exportExcelModeEnum.CURRENT_SHOW">导出Excel(当前字段)</a-doption>
        <a-doption :value="exportExcelModeEnum.ALL">导出Excel(全部字段)</a-doption>
      </template>
    </a-dropdown>
  </div>
</template>
<script setup lang="ts">
  import { exportExcelModeEnum } from '@/views/register/registration-and-batch-info-query/types'
  import ExportJsonExcel from 'js-export-excel'

  const Emit = defineEmits(['handleSearch', 'exportExcel'])
  /**
   *  导出excel文件
   */

  const exportExcelMode = ref(exportExcelModeEnum.ALL)
  const handleSelectExportExcelMode = (value) => {
    exportExcelMode.value = value
    exportExcel(exportExcelMode.value)
  }

  const exportFunc = (_data: Array<[]>, option: { [key in string]: any }) => {
    let [targetHeader, sheetData]: [[], []] = [[], []]
    const { fileName } = option
    if (Array.isArray(_data) && _data.length > 0) {
      const [firstArr] = _data.splice(0, 1)
      targetHeader = firstArr
    }
    _data.forEach((items) => {
      if (Array.isArray(items)) {
        const targetRowInfo = {}
        items.forEach((innerItem, index) => {
          targetRowInfo[targetHeader[index]] = innerItem
        })
        sheetData.push(targetRowInfo as unknown as never)
      }
    })

    const datas = [
      {
        sheetData,
        sheetName: fileName,
        sheetHeader: targetHeader,
        sheetFilter: targetHeader
      }
    ]

    const toExcel = new ExportJsonExcel({
      fileName,
      datas
    })
    //   调用保存方法
    toExcel.saveExcel()
  }
  const exportExcel = (exportExcelMode: exportExcelModeEnum) => {
    Emit('exportExcel', exportExcelMode, exportFunc)
  }
</script>
//组件内部维护的不同 tab 对应的信息
  let TabMapDataSource = reactive({
    [TabsEnum.REGISTER]: {
      //展示的cols
      showColumns: [],
      //编辑的cols信息 (dataIndexArr)
      selectShowColumns: []
    },
    [TabsEnum.BATCH]: {
      showColumns: [],
      selectShowColumns: []
    }
  })
const showCols = computed(() => {
    return {
      productShowCols: toRaw(TabMapDataSource[TabsEnum.REGISTER].showColumns),
      batchShowCols: toRaw(TabMapDataSource[TabsEnum.BATCH].showColumns)
    }
  })

defineExpose({
  selectedRowKeys,
  showCols,
  clearCacheData,
  currentAllCols
})

defineExpose

我们从父组件获取子组件实例通过ref

<Menu ref="menus"></Menu>
const menus = ref(null)

然后打印menus.value 发现没有任何属性
这时候父组件想要读到子组件的属性可以通过 defineExpose暴露

const list = reactive<number[]>([4, 5, 6])
defineExpose({
    list
})

这样父组件就可以读到了。