react + xlsx 表格导出功能 全部实现

需求 : 在react中将表格多样化导出 , 既可以全部导出所有表格数据 , 也可以选择性导出 + 导出可以选择三种样式

选择了全部 , 不能选其他

全部导出

部分导出

1 导出按钮下拉弹出三种导出格式

javascript 复制代码
           <Dropdown
                menu={{
                  items: [
                    {
                      label: (
                        <a
                          onClick={() => {
                            setFormat('xlsx')
                          }}
                        >
                          导出Excel
                        </a>
                      ),
                      key: '1'
                    },

                    {
                      label: (
                        <a
                          onClick={() => {
                            setFormat('csv')
                          }}
                        >
                          导出CSV
                        </a>
                      ),
                      key: '2'
                    },
                    {
                      label: (
                        <a
                          onClick={() => {
                            setFormat('txt')
                          }}
                        >
                          导出TXT
                        </a>
                      ),
                      key: '3'
                    }
                  ]
                }}
              >
                <Button
                  type='primary'
                  ghost
                  icon={
                    <img
                      src={derive}
                      alt=''
                      style={{ width: 11, height: 10, pointerEvents: 'none' }}
                    />
                  }
                  style={{ width: 125, height: 40, fontSize: 16 }}
                >
                  导出
                </Button>
              </Dropdown>

2 多选框内选中全部时 , 其他不被选中 ; 选中其他时 , 全部不被选中

javascript 复制代码
// Checkbox.Group不必被盒子包裹 , 而Radio.Group必须得被盒子包裹
            <Checkbox.Group value={value2} onChange={value2Change}>
              <Checkbox style={{ marginTop: 20, marginRight: 15 }} value={1}>
                全部
              </Checkbox>

              <Checkbox
                style={{ marginTop: 20, marginRight: 15 }}
                value={'工单编号'}
              >
                工单编号
              </Checkbox>
              <Checkbox
                style={{ marginTop: 20, marginRight: 15 }}
                value={'地点'}
              >
                地点
              </Checkbox>
              <Checkbox
                style={{ marginTop: 20, marginRight: 15 }}
                value={'位置'}
              >
                位置
              </Checkbox>
              <Checkbox
                style={{ marginTop: 20, marginRight: 15 }}
                value={'设备类型'}
              >
                设备类型
              </Checkbox>
              <Checkbox
                style={{ marginTop: 20, marginRight: 15 }}
                value={'品牌/型号'}
              >
                品牌/型号
              </Checkbox>
              <Checkbox
                style={{ marginTop: 20, marginRight: 15 }}
                value={'设备编号'}
              >
                设备编号
              </Checkbox>
              <Checkbox
                style={{ marginTop: 20, marginRight: 15 }}
                value={'报修单状态'}
              >
                报修单状态
              </Checkbox>
            </Checkbox.Group>



  const [value2, setValue2] = useState([1])

  const value2Change = checkedValues => {
    if (checkedValues.includes(1)) {
      // 如果"全部"被选中,则清除所有其他选项,仅保留"全部"
      if (checkedValues.length > 1) {
        // 如果还有其他选项被选中,则清空数组,仅保留"全部"
        setValue2([1])
      } else {
        // 如果只有"全部"被选中,保持现状
        setValue2(checkedValues)
      }
    } else {
      // 如果"全部"未被选中,且当前有其他选项被选中,则直接更新状态
      // 注意:这里确保了当选择其他任何选项时,"全部"不会被选中
      setValue2(checkedValues.filter(value => value !== 1))
    }
  }

// 拿到实时的value2
  useEffect(() => {
    console.log('value2', value2)
  }, [value2])

3 导出的具体实现

3.1 data中定义了数据结构 , column是表格列的数据 , 我需要将data中列的数据转成column中title的值

流程图如下

javascript 复制代码
  const [data, setData] = useState([
    {
      key: '1',
      username: 'B123',
      realname: '北京总部大厦',
      sex: '三层会议室',
      birthday: '打印机',
      phone: 'AB-0312',
      orgCodeTxt: 'AB0312',
      status: 1,
      action: 1
    },
...
] 


 const columns0 = [
    {
      title: <span onClick={() => handleSort('username')}>工单编号</span>,
      dataIndex: 'username',
      align: 'center',
      key: 'username'
    },
...
] 



 // 遍历columns0,为data中的每个对象创建一个仅含转换后数据的新对象
  let data1 = data.map(item => {
    const newItem = {}

    columns0.forEach(column => {
      const title = getTitleText(column)
      const dataIndex = column.dataIndex

      if (title && item.hasOwnProperty(dataIndex)) {
        // 对于'status'列,直接应用状态码转换逻辑
        newItem[title] =
          column.dataIndex === 'status'
            ? getStatusText(item[dataIndex])
            : item[dataIndex]
      }
    })

    return newItem
  })

  console.log('data1', data1)
// 这样打印出data1的值 没有拿到想要的title 而是[object object]

3.2 遇到了object object的问题 , 没拿到想要的title值

原因 : column的title不是单纯的汉字 , 还被其他的img和span包裹着 , 导致没提取到正确的title
解决 : 根据typeof进行判断 , 是字符串直接返回 ; 或者.props && .props.children是字符串直接返回 ; 再或者就返回空

javascript 复制代码
      // 直接从columns配置中提取标题文本,忽略任何额外的JSX元素
      function getTitleText (column) {
        if (typeof column.title === 'string') {
          return column.title
        } else if (
          column.title.props &&
          typeof column.title.props.children === 'string'
        ) {
          return column.title.props.children
        }
        return '' // 如果无法直接提取文本,则返回空字符串
      }

      // 提炼状态码转换逻辑为独立函数
      function getStatusText (status) {
        switch (status) {
          case 1:
            return '维修中'
          case 2:
            return '待派单'
          case 3:
            return '待确认'
          case 4:
            return '关单'
          default:
            return status.toString() // 或者其他默认处理
        }
      }

      // 遍历columns0,为data中的每个对象创建一个仅含转换后数据的新对象
      let data1 = data.map(item => {
        const newItem = {}

        columns0.forEach(column => {
          const title = getTitleText(column)
          const dataIndex = column.dataIndex

          if (title && item.hasOwnProperty(dataIndex)) {
            // 对于'status'列,直接应用状态码转换逻辑
            newItem[title] =
              column.dataIndex === 'status'
                ? getStatusText(item[dataIndex])
                : item[dataIndex]
          }
        })

        return newItem
      })

      console.log('data1', data1)

3.3 导出实现

javascript 复制代码
import * as XLSX from 'xlsx'
import { saveAs } from 'file-saver'



  const [format, setFormat] = useState('')

  const handleDerive = columnsToExport => {
      let dataSource = []
      if (columnsToExport.includes(1)) {
        dataSource = data1
      } else {
        dataSource = data1.map(row => {
          const newRow = {}
          columnsToExport.forEach(column => {
            newRow[column] = row[column]
          })
          return newRow
        })
      }
      const worksheet = XLSX.utils.json_to_sheet(dataSource)
      const workbook = XLSX.utils.book_new()
      XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1')

      let fileExtension = ''
      let mimeType = ''

      console.log('format', format)

      if (format === 'xlsx') {
        setDeriveMoadal(true)
        setFormat('xlsx')
        // 导出为 Excel 文件
        fileExtension = 'xlsx'
        mimeType =
          'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
      } else if (format === 'csv') {
        // 导出为 CSV 文件
        const csv = XLSX.utils.sheet_to_csv(worksheet)
        const csvData = new Blob([csv], { type: 'text/csv;charset=utf-8;' })
        saveAs(csvData, 'export.csv')
        return
      } else if (format === 'txt') {
        // 导出为 TXT 文件
        const txt = XLSX.utils.sheet_to_txt(worksheet)
        const txtData = new Blob([txt], { type: 'text/plain;charset=utf-8;' })
        saveAs(txtData, 'export.txt')
        return
      }

      const excelBuffer = XLSX.write(workbook, {
        bookType: format,
        type: 'array'
      })
      const excelBlob = new Blob([excelBuffer], { type: mimeType })

      const url = window.URL.createObjectURL(excelBlob)
      const link = document.createElement('a')
      link.href = url
      link.download = `export.${fileExtension}`
      document.body.appendChild(link)
      link.click()
      document.body.removeChild(link)
      window.URL.revokeObjectURL(url)
    }
  }
  useEffect(() => {
    if (format) {
      handleDerive()
    }
  }, [format])
相关推荐
卸任19 小时前
Vibe coding工具使用心得
前端·ai编程
brien920 小时前
为什么模型标称 1M,Google Antigravity 聊到 256K 却突然失忆?我们给它做了个原生行内监控圆环
前端
修炼的dance20 小时前
录屏关键帧间隔多少合适?六档实测,2秒到10秒小了75.6%
前端
Lank_M20 小时前
关键帧从1秒拉到10秒,录屏小87.3%,摄像头小10.8%
前端
补码缩写20 小时前
用 ffprobe 拆录屏,八成字节在 29 个关键帧上
前端
百思不得小李20 小时前
一个复杂的动态表单是如何实现的
前端·设计模式
陈随易20 小时前
Cmder 停更 17 个月,2.0 支持 Windows Terminal
前端·后端·程序员
清晨Coding21 小时前
删了 Cookie 还能自动登录?Google系网站登录态自动恢复的原理分析
javascript
风骏时光牛马21 小时前
前沿视界
前端
kyriewen21 小时前
我扫了 814 个依赖,97 个还敢要 Node 10
前端·javascript·程序员