<script>
import { defineComponent, ref, onMounted, watch } from 'vue-demi'
import Spreadsheet from 'x-data-spreadsheet'
import _ from 'lodash'
import * as Excel from 'exceljs/dist/exceljs'
import tinycolor from 'tinycolor2'
import { getData } from './src/excel'
import './src/index.css'

export default defineComponent({
  name: 'VueOfficeExcel',
  props: {
    src: [String, ArrayBuffer, Blob],
    requestOptions: {
      type: Object,
      default: () => ({})
    }
  },
  emits: ['rendered', 'error', 'cell-selected'],
  setup (props, { emit }) {
    // console.log(Excel)
    const rootRef = ref(null)
    let xs = null
    const Ho = ['#000000', '#FFFFFF', '#FF0000', '#00FF00', '#0000FF', '#FFFF00', '#FF00FF', '#00FFFF', '#000000', '#FFFFFF', '#FF0000', '#00FF00', '#0000FF', '#FFFF00', '#FF00FF', '#00FFFF', '#800000', '#008000', '#000080', '#808000', '#800080', '#008080', '#C0C0C0', '#808080', '#9999FF', '#993366', '#FFFFCC', '#CCFFFF', '#660066', '#FF8080', '#0066CC', '#CCCCFF', '#000080', '#FF00FF', '#FFFF00', '#00FFFF', '#800080', '#800000', '#008080', '#0000FF', '#00CCFF', '#CCFFFF', '#CCFFCC', '#FFFF99', '#99CCFF', '#FF99CC', '#CC99FF', '#FFCC99', '#3366FF', '#33CCCC', '#99CC00', '#FFCC00', '#FF9900', '#FF6600', '#666699', '#969696', '#003366', '#339966', '#003300', '#333300', '#993300', '#993366', '#333399', '#333333', '#FFFFFF']
    function renderExcel (buffer) {
      let col = 0
      let row = 0
      try {
        const wb = new Excel.Workbook()
        // 微软的 Excel ColorIndex 一个索引数字对应一个颜色
        wb.xlsx.load(buffer).then(workbook => {
          // console.log(workbook)
          const workbookData = []
          // 遍历工作簿
          workbook.eachSheet((sheet) => {
            col = sheet.columnCount > col ? sheet.columnCount : col
            row = sheet.rowCount > row ? sheet.rowCount : row
            if (sheet.state === 'hidden') {
              return
            }
            // console.log(sheet)
            // 构造x-data-spreadsheet 的 sheet 数据源结构
            const sheetData = { name: sheet.name, styles: [], rows: {}, merges: [] }
            // 收集合并单元格信息
            const mergeAddressData = []
            for (const mergeRange in sheet._merges) {
              sheetData.merges.push(sheet._merges[mergeRange].shortRange)
              const mergeAddress = {}
              // 合并单元格起始地址
              mergeAddress.startAddress = sheet._merges[mergeRange].tl
              // 合并单元格终止地址
              mergeAddress.endAddress = sheet._merges[mergeRange].br
              // Y轴方向跨度
              mergeAddress.YRange = sheet._merges[mergeRange].model.bottom - sheet._merges[mergeRange].model.top
              // X轴方向跨度
              mergeAddress.XRange = sheet._merges[mergeRange].model.right - sheet._merges[mergeRange].model.left
              mergeAddressData.push(mergeAddress)
            }
            if (sheet.columns) {
              sheetData.cols = {}
              for (let i = 0; i < sheet.columns.length; i++) {
                sheetData.cols[i.toString()] = {}
                if (sheet.columns[i].width) {
                  // 不知道为什么从 exceljs 读取的宽度显示到 x-data-spreadsheet 特别小, 这里乘以8
                  sheetData.cols[i.toString()].width = sheet.columns[i].width * 8
                } else {
                  // 默认列宽
                  sheetData.cols[i.toString()].width = 100
                }
              }
            }
            // 遍历行
            sheet.eachRow({
              includeEmpty: true,
              formulae: true // 确保公式被解析
            }, (row, rowIndex) => {
              console.log({ ...row })
              sheetData.rows[(rowIndex - 1).toString()] = { cells: {} }
              // includeEmpty = false 不包含空白单元格
              row.eachCell({ includeEmpty: true }, function (cell, colNumber) {
                let cellText = ''
                // console.log(cell.type, cellText)
                switch (cell.type) {
                  case Excel.ValueType.Null: // 0
                    cellText = null // 或者处理为空的情况
                    break
                  case Excel.ValueType.Merge: // 1
                    cellText = null // 或者处理为空的情况
                    break
                  case Excel.ValueType.Number: // 2
                    cellText = cell.value // 直接获取数字
                    break
                  case Excel.ValueType.String: // 3
                    cellText = cell.value // 直接获取字符串
                    break
                  case Excel.ValueType.Date: // 4
                    // switch (cell.style.numFmt) {
                    //   case 'mm-dd-yy':
                    //     Lo(cell.value).format('YYYY/MM/DD')
                    //     break
                    //   case '[$-F800]dddd, mmmm dd, yyyy':
                    //     Lo(cell.value).format('YYYY年M月D日 ddd')
                    //     break
                    //   case 'm"月"d"日";@':
                    //     Lo(cell.value).format('M月D日')
                    //     break
                    //   case 'm/d/yy "h":mm':
                    //     Lo(cell.value).subtract(8, 'hour').format('YYYY/M/DD HH:mm')
                    //     break
                    //   case 'h:mm;@':
                    //     Lo(cell.value).format('HH:mm')
                    //     break
                    //   default:
                    //     Lo(cell.value).format('YYYY-MM-DD')
                    //     break
                    // }
                    cellText = new Date(cell.value) // 获取日期对象
                    break
                  case Excel.ValueType.Hyperlink: // 5
                    // console.log('超链接', cell.text, cell.value)
                    cellText = cell.text // 获取日期对象
                    break
                  case Excel.ValueType.Formula: // 6
                    cellText = cell.value.result?.error || cell.value.result // 获取公式值
                    break
                  case Excel.ValueType.SharedString: // 7
                    cellText = cell.value
                    break
                  case Excel.ValueType.RichText: // 8
                    cellText = cell.value
                    break
                  case Excel.ValueType.Boolean: // 9
                    cellText = cell?.text ? cell?.text.toUpperCase() : '' // 直接获取布尔值
                    break
                  default:
                    cellText = cell.value
                }
                // switch (cell.type) {
                //   case 2:
                //     // 数字格式
                //     try {
                // if (cell?.style?.numFmt) {
                //   if (cell.style.numFmt.endsWith('%')) {
                //     const n = cell.style.numFmt.match(/\.(\d+)%/)
                //     cellText = n ? (100 * cell.value).toFixed(n[1].length) + '%' : 100 * cell.value + '%'
                //   } else if (/0(\.0+)?/.test(cell.style.numFmt)) {
                //     let i = ''
                //     if (cell.style.numFmt.startsWith('$')) {
                //       i = '$'
                //     }
                //     if (cell.style.numFmt.startsWith('"¥')) {
                //       i = '¥'
                //     }
                //     if (cell.value === 0 && cell.style.numFmt.startsWith('_')) {
                //       cellText = '-'
                //     }

                //     let o = cell.style.numFmt.match(/0\.(0+)(_|;|$)/)

                //     o = o ? o[1].length : 0
                //     let a = cell?.value ? cell.value.toFixed(o) + '' : ''
                //     if (cell.style.numFmt.includes('#,##')) {
                //       for (let s = (a = a.split('.'))[0].split('').reverse(), u = [], l = 0; l < s.length; l++) {
                //         u.push(s[l])
                //         if ((l + 1) % 3 === 0 && l < s.length - 1 && s[l + 1] !== '-') {
                //           u.push(',')
                //         }
                //         a[0] = u.reverse().join('')
                //         a = a.join('.')
                //       }
                //     }
                //     cellText = i + a
                //   } else {
                //     cellText = cell.value
                //   }
                // }
                //       cellText = cell.value + ''
                //     } catch (error) {
                //       console.log(error)
                //     }
                //     break
                //   case 3:
                //     cellText = cell.value
                //     break
                //   case 4:
                //     // 时间格式
                //     break
                //   case 5:
                //     cellText = cell.value?.text || ''
                //     break
                //   case 6:
                //     cellText = cell.value?.result?.error || cell.value?.result || ''
                //     break
                //   case 8:
                //     cellText = cell?.text || ''
                //     break
                //   case 9:
                //     cellText = cell?.text ? cell?.text.toUpperCase() : ''
                //     break
                //   default:
                //     cellText = cell.value
                //     break
                // }
                // let cellText = ''
                // if (cell.value && cell.value.result) {
                //   // Excel 单元格有公式
                //   cellText = cell.value.result?.error || cell.value.result
                // } else if (cell.value && cell.value.richText) {
                //   // Excel 单元格是多行文本
                //   for (const text in cell.value.richText) {
                //     // 多行文本做累加
                //     cellText += cell.value.richText[text].text
                //   }
                // } else {
                //   // Excel 单元格无公式
                //   if (typeof cell.value === 'object' && cell.value !== null) {
                //     cellText = ''
                //   } else {
                //     cellText = cell.value
                //   }
                // }
                // 解析单元格,包含样式
                //* ********************单元格存在背景色******************************
                // 单元格存在背景色
                let backGroundColor = null
                if (cell.style.fill && cell.style.fill.fgColor && cell.style.fill.fgColor.argb) {
                  // 8位字符颜色先转rgb再转16进制颜色
                  backGroundColor = ((val) => {
                    val = val.trim().toLowerCase() // 去掉前后空格
                    const color = {}
                    // try {
                    const argb = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(val)
                    color.r = parseInt(argb[2], 16)
                    color.g = parseInt(argb[3], 16)
                    color.b = parseInt(argb[4], 16)
                    color.a = parseInt(argb[1], 16) / 255
                    return tinycolor(`rgba(${color.r}, ${color.g}, ${color.b}, ${color.a})`).toHexString()
                    // } catch (e) {
                    //   //
                    // }
                  })(cell.style.fill.fgColor.argb)
                } else if (cell.style.fill && cell.style.fill.fgColor && cell.style.fill.fgColor.indexed) {
                  backGroundColor = Ho[cell.style.fill.fgColor.indexed] || '#C7C9CC'
                }

                if (backGroundColor) {
                  cell.style.bgcolor = backGroundColor
                }
                //* ************************************************************************** */

                //* ********************字体存在背景色******************************
                // 字体颜色
                let fontColor = null
                if (cell.style.font && cell.style.font.color && cell.style.font.color.argb) {
                  // 8位字符颜色先转rgb再转16进制颜色
                  fontColor = ((val) => {
                    val = val.trim().toLowerCase() // 去掉前后空格
                    const color = {}
                    // try {
                    const argb = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(val)
                    color.r = parseInt(argb[2], 16)
                    color.g = parseInt(argb[3], 16)
                    color.b = parseInt(argb[4], 16)
                    color.a = parseInt(argb[1], 16) / 255
                    return tinycolor(`rgba(${color.r}, ${color.g}, ${color.b}, ${color.a})`).toHexString()
                    // } catch (e) {
                    //   //
                    // }
                  })(cell.style.font.color.argb)
                } else if (cell.style.font && cell.style.font.color && cell.style.font.color.indexed) {
                  fontColor = Ho[cell.style.font.color.indexed] || '#C7C9CC'
                }
                if (fontColor) {
                  cell.style.color = fontColor
                }

                // exceljs 对齐的格式转成 x-date-spreedsheet 能识别的对齐格式
                if (cell.style.alignment && cell.style.alignment.horizontal) {
                  cell.style.align = cell.style.alignment.horizontal
                  cell.style.valign = cell.style.alignment.vertical
                }

                const borders = {}
                if (cell.style.border) {
                  Object.keys(cell.style.border).forEach((t) => {
                    const r = cell.style.border[t]
                    let n = '#000000'
                    if (typeof r.color === 'string') {
                      n = r.color
                    }
                    borders[t] = [r.style || 'thin', n]
                  })
                }

                // 处理合并单元格
                const mergeAddress = _.find(mergeAddressData, function (o) { return o.startAddress === cell._address })
                const _style = {
                  text: cellText,
                  style: 0,
                  border: borders
                }
                if (cell.style.numFmt) {
                  _style.value = cellText
                  Object.assign(_style, { numFmt: cell.style.numFmt })
                }
                if (mergeAddress) {
                  // 遍历的单元格属于合并单元格
                  if (cell.master.address !== mergeAddress.startAddress) {
                    // 不是合并单元格中的第一个单元格不需要计入数据源
                    return
                  }
                  // 说明是合并单元格区域的起始单元格
                  Object.assign(_style, { merge: [mergeAddress.YRange, mergeAddress.XRange] })
                }
                sheetData.rows[(rowIndex - 1).toString()].cells[(colNumber - 1).toString()] = _style
                // 解析单元格,包含样式
                sheetData.styles.push(cell.style)
                // 对应的style存放序号
                sheetData.rows[(rowIndex - 1).toString()].cells[(colNumber - 1).toString()].style = sheetData.styles.length - 1
              })
            })
            workbookData.push(sheetData)
            console.log('workbookData', workbookData)
          })
          xs.options.row.len = row + 10
          xs.options.col.len = col + 10
          xs.loadData(workbookData)
          // 设置宽度 长度
          emit('rendered', xs)
        }).catch(error => {
          xs.loadData({})
          emit('error', error)
        })
      } catch (e) {
        xs.loadData({})
        emit('error', e)
      }
    }
    onMounted(() => {
      // console.log('加载')
      xs = new Spreadsheet(rootRef.value, {
        mode: 'read',
        showToolbar: false,
        showContextmenu: false,
        view: {
          height: () => rootRef.value?.offsetHeight ?? 0,
          width: () => {
            // console.log('rootRef', rootRef.value)
            return rootRef.value?.offsetWidth ?? 0
          }
        },
        row: {
          len: 500,
          height: 25
        },
        col: {
          len: 1000,
          width: 100,
          indexWidth: 60,
          minWidth: 60
        }
      }).loadData({})
      if (props.src) {
        getData(props.src, props.requestOptions).then(renderExcel).catch(e => {
          xs.loadData({})
          emit('error', e)
        })
      }
      // 单元格点击事件
      xs.on('cell-selected', (cell, ri, ci) => {
        // const a = xs.cell(ri, ci)
        emit('cell-selected', cell, ri, ci)
      })
      xs.bottombar.menuEl.el.onclick = evt => {
        if (evt.target.localName === 'ul') {
          return
        }
        emit('click:bottom-bar', evt.target.innerText)
      }
    })

    watch(() => props.src, () => {
      if (props.src) {
        getData(props.src).then(renderExcel).catch(e => {
          xs.loadData({})
          emit('error', e)
        })
      } else {
        xs.loadData({})
      }
    })

    // const setView = (sheetName) => {
    //   xs.bottombar.updateFunc()
    // }
    const getRow = (index) => {
      const data = xs.sheet.data.rows.get(index)
      // console.log(data)
      if (!data) {
        return
      }
      const item = Object.values(data.cells)
      let merge = [item.length, 0]
      return item.filter((e, i) => {
        if (e.merge && e.merge.length) {
          merge = e.merge.map(e => e + i)
        }
        return (i < merge[0] || i >= merge[1]) && e.text
      }).map(e => e.text)
    }
    const root = () => {
      return xs
    }
    return {
      rootRef,
      root,
      getRow
    }
  }
})
</script>

<template>
  <div class="vue-office-excel" ref="rootRef"></div>
</template>
<style lang="less">
.vue-office-excel {
  overflow: auto;
  // .x-spreadsheet-menu {
  //   overflow: auto;
  // }

  .x-spreadsheet-menu {
    display: flex;
    overflow-y: hidden;
    overflow-x: auto;
    li:first-of-type {
      display: none;
    }
  }
}
</style>
