<template>
  <div>
    <div>
      <hot-table ref="table" :settings="hotSettings"></hot-table>
    </div>
    <div class="btn-group">
      <button
        v-for="sheet in sheets"
        :key="sheet.id"
        style="padding: 0 30px"
        :type="sheetIndex === sheet.id ? 'primary' : 'default'"
        @click="handleSheet(sheet.id)"
      >
        {{ sheet.name }}
      </button>
    </div>
  </div>
</template>

<script>
import { HotTable } from '@handsontable/vue'
import Handsontable from 'handsontable'
import { registerLanguageDictionary, zhCN } from 'handsontable/i18n'
import { indexedColors } from './color'
import { camelCase, captain, fixMatrix } from './util'

// 注册中文
registerLanguageDictionary(zhCN)

// 边框类型
const borders = ['left', 'right', 'top', 'bottom']

export default {
  name: 'HelloWorld',
  props: {
    msg: String,
    workbook: Object
  },
  components: { HotTable },
  data() {
    return {
      sheetIndex: 0,
      selection: {
        style: {},
        ranges: []
      }
    }
  },
  created() {
    // 注册自定义渲染
    Handsontable.renderers.registerRenderer(
      'styleRender',
      (hotInstance, TD, row, col, prop, value, cell) => {
        Handsontable.renderers.getRenderer('text')(
          hotInstance,
          TD,
          row,
          col,
          prop,
          value,
          cell
        )
        if (this.ws && cell.style) {
          const {
            style: { alignment: { wrapText } = {}, border, fill, font }
          } = cell
          const style = TD.style
          if (font) {
            if (font.bold) style.fontWeight = 'bold'
            if (font.size) style.fontSize = `${font.size}px`
          }
          if (fill) {
            if (fill.bgColor) {
              const { argb, indexed } = fill.bgColor
              style.backgroundColor = `#${argb || indexedColors[indexed]}`
            }
            if (fill.fgColor) {
              const { theme, indexed } = fill.fgColor
              if (theme && this.themeColors) {
                const color = this.themeColors[theme + 1]
                if (color) {
                  style.color = `#${color}`
                }
              }
              if (indexed) {
                style.color = `#${indexedColors[indexed]}`
              }
            }
          }
          if (border) {
            borders
              .map((key) => ({ key, value: border[key] }))
              .filter((v) => v.value)
              .forEach((v) => {
                const {
                  key,
                  value: { style: borderStyle }
                } = v
                const prefix = `border${captain(key)}`
                if (borderStyle === 'thin') {
                  style[`${prefix}Width`] = '1px'
                } else {
                  style[`${prefix}Width`] = '2px'
                }
                style[`${prefix}Style`] = 'solid'
                style[`${prefix}Color`] = '#000'
              })
          }
        }
        // 启用了内联css，直接赋值
        if (cell.css) {
          const style = TD.style
          const { css } = cell
          Object.keys(css).forEach((key) => {
            const k = camelCase(key)
            style[k] = css[key]
          })
        }
      }
    )
  },
  watch: {
    workbook() {
      this.parseTheme()
      this.updateTable()
    }
  },
  computed: {
    hotSettings() {
      return {
        language: 'zh-CN',
        readOnly: true,
        data: this.data,
        cell: this.cell,
        mergeCells: this.merge,
        colHeaders: true,
        rowHeaders: true,
        height: 'calc(100vh - 107px)',
        // contextMenu: true,
        // manualRowMove: true,
        // 关闭外部点击取消选中时间的行为
        outsideClickDeselects: false,
        // fillHandle: {
        //   direction: 'vertical',
        //   autoInsertRow: true
        // },
        // afterSelectionEnd: this.afterSelectionEnd,
        // bindRowsWithHeaders: 'strict',
        licenseKey: 'non-commercial-and-evaluation'
      }
    },
    ws() {
      const { workbook: { getWorksheet } = {} } = this
      if (getWorksheet) {
        const index = this.sheetIndex || this.sheets[0].id
        return this.workbook.getWorksheet(index)
      }
      return null
    },
    sheets() {
      if (this.workbook.worksheets) {
        return this.workbook.worksheets.filter((sheet) => sheet._rows.length)
      }
      return []
    },
    merge() {
      const { ws: { _merges: merges = {} } = {} } = this
      return Object.values(merges).map(({ left, top, right, bottom }) => {
        // 构建区域
        return {
          row: top - 1,
          col: left - 1,
          rowspan: bottom - top + 1,
          colspan: right - left + 1
        }
      })
    },
    data() {
      return fixMatrix(
        this.ws.getRows(1, this.ws.actualRowCount).map((row) =>
          row._cells.map((item) => {
            const value = item.model.value
            if (value) {
              // 处理单元格内原始数据
              if (value.richText) {
                let text = value.richText.text
                if (Array.isArray(value.richText)) {
                  text = value.richText.reduce(
                    (prev, curr) => prev + curr.text,
                    ''
                  )
                }
                return text
              }
              return value
            }
            return ''
          })
        ),
        this.cols.length
      )
    },
    cols() {
      return this.ws.columns.map((item) => item.letter)
    },
    columns() {
      return this.ws.columns.map((item) => ({
        ...(item.width
          ? { width: item.width < 100 ? 100 : item.width }
          : { width: 100 }),
        className: this.alignToClass(item.alignment || {}),
        renderer: 'styleRender'
      }))
    },
    cell() {
      return this.ws.getRows(1, this.ws.actualRowCount).flatMap((row, ri) => {
        return row._cells
          .map((cell, ci) => {
            if (cell.style) {
              return {
                row: ri,
                col: ci,
                ...(cell.alignment
                  ? { className: this.alignToClass(cell.alignment) }
                  : {}),
                style: cell.style
              }
            }
          })
          .filter((i) => i)
      })
    },
    border() {
      return this.ws.getRows(1, this.ws.actualRowCount).flatMap((row, ri) => {
        return row._cells
          .map((cell, ci) => {
            if (cell.style && cell.style.border) {
              const border = cell.style.border
              const keys = Object.keys(border)
              if (keys.length) {
                return {
                  row: ri,
                  col: ci,
                  ...keys.reduce((result, key) => {
                    result[key] = {
                      width: 1,
                      color: `#${(border.color &&
                        indexedColors[border.color.indexed]) ||
                        border.argb ||
                        '000000'}`
                    }
                    return result
                  }, {})
                }
              }
            }
          })
          .filter((i) => i)
      })
    }
  },
  methods: {
    hotTable() {
      return this.$refs.table.hotInstance
    },
    updateTable() {
      this.hotTable().updateSettings({
        mergeCells: this.merge,
        data: this.data,
        colHeaders: this.cols,
        columns: this.columns,
        cell: this.cell
        // customBorders: this.border,
      })
    },
    alignToClass({ horizontal, vertical }) {
      return [horizontal, vertical]
        .filter((i) => i)
        .map((key) => `ht${key.charAt(0).toUpperCase()}${key.slice(1)}`)
        .join(' ')
    },
    parseTheme() {
      const theme = this.workbook._themes.theme1
      const parser = new DOMParser()
      if (theme) {
        const doc = parser.parseFromString(theme, 'text/xml')
        const [{ children = [] } = {}] = doc.getElementsByTagName('a:clrScheme')
        this.themeColors = [...children]
          .flatMap((node) => [...node.getElementsByTagName('a:srgbClr')])
          .map((node) => node.getAttribute('val'))
          .filter((i) => i)
      }
    },
    // 切换sheet
    handleSheet(index) {
      if (this.sheetIndex !== index) {
        this.sheetIndex = index
        this.$nextTick(() => {
          this.updateTable()
        })
      }
    },
    // 处理样式
    handleStyle(style, { type, key }) {
      this.selection.style = style
      const hot = this.hotTable()
      // 暂停自定义渲染逻辑
      hot.suspendRender()
      this.selection.ranges.forEach(({ r, c }) => {
        const { css = {} } = hot.getCellMeta(r, c)
        const merged = { ...css }
        // 差量赋值，按照excel标准
        if (type === 'remove') {
          delete merged[key]
        } else if (type === 'add') {
          merged[key] = style[key]
        }
        hot.setCellMetaObject(r, c, {
          css: merged
        })
      })
      // 手动渲染
      hot.render()
      // 恢复自动渲染逻辑
      hot.resumeRender()
    },
    // 选中区域回调
    afterSelectionEnd(row, column, row2, column2, selectionLayerLevel) {
      const ranges = []
      for (let r = row; r <= row2; r++) {
        for (let c = column; c <= column2; c++) {
          ranges.push({ r, c })
        }
      }
      // 获得左上角的元数据，初始化一些状态
      const { css = {} } = this.hotTable().getCellMeta(row, column)
      this.selection.style = css
      this.selection.ranges = ranges
    }
  }
}
</script>

<style>
.handsontable {
  font-size: 13px;
  color: #222;
}
</style>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.sheet-btn.active {
  background-color: aquamarine;
}

.btn-group {
  margin-top: 5px;
  display: block;
  border-bottom: 1px solid grey;
  background-color: lightblue;
}

.table-tool {
  padding: 8px 0;
  border-top: 1px solid black;
}
</style>
