<template>
  <view class="ml-table" :class="[className || '', {'is-border': border, 'is-ellipsis' : showOverflow}]">
    <view v-if="$slots.top" class="ml-table-top-warpper">
      <slot name="top"></slot>
    </view>
    <view class="ml-table-warpper" :style="tableStyle">
      <view class="table-header">
        <view class="table-column-header">
          <view class="ml-table-tr">
            <view v-for="(column, columnIndex) in tableColumn" :key="columnIndex" class="table-th" :class="{'is-sort': !!column.sortable}" :style="{width: getColWidth(column)}" @click="chickThEvent({ column, columnIndex })">
              <view class="th-title">{{ column.title }}</view>
              <view class="th-sort" v-if="column.sortable">
                <view class="asc-btn" :class="{active: column.order === 'asc'}" @click.stop="sortEvent({column, columnIndex}, 'asc')">
                  <ml-icon name="arrow-up-filling"></ml-icon>
                </view>
                <view class="desc-btn" :class="{active: column.order === 'desc'}" @click.stop="sortEvent({column, columnIndex}, 'desc')">
                  <ml-icon name="arrow-down-filling"></ml-icon>
                </view>
              </view>
            </view>
          </view>
        </view>
        <view v-if="headerData && headerData.length" class="table-body-top">
          <view class="ml-table-tr" v-for="(row, rowIndex) in headerData" :key="rowIndex">
            <view class="ml-table-td" v-for="(column, columnIndex) in tableColumn" :key="columnIndex" :style="{width: getColWidth(column)}" @click="cellHeaderTdClick({ type: 'header', row, rowIndex, column, columnIndex })">
              <view :class="['ml-table-cell', column.align ? `align-${column.align}` : '', getHeaderCellClass({ type: 'header', row, rowIndex, column, columnIndex })]">
                <ml-amount-text v-if="column.cellRender && column.cellRender.name === 'MlAmountText'" :value="row[column.field]" :simplify="column.cellRender.props ? column.cellRender.props.simplify : false"></ml-amount-text>
                <text v-else>{{ getHeaderCellValue({ type: 'header', row, rowIndex, column, columnIndex }) }}</text>
              </view>
            </view>
          </view>
        </view>
      </view>
      <view class="table-body-contnet">
        <slot>
          <view class="ml-table-tr" v-for="(row, rowIndex) in tableData" :key="rowIndex">
            <view class="ml-table-td" v-for="(column, columnIndex) in tableColumn" :key="columnIndex" :style="{width: getColWidth(column), height: getColHeight(column)}" @click="cellTdClick({ type: 'body', row, rowIndex, column, columnIndex })">
              <view :class="['ml-table-cell', column.align ? `align-${column.align}` : '', getCellClass({ type: 'body', row, rowIndex, column, columnIndex })]">
                <text v-if="column.type === 'seq'">{{ rowIndex + 1 }}</text>
                <text v-else-if="column.cellRender && column.cellRender.name === 'Underline'" class="is-underline">{{ getCellValue({ type: 'body', row, rowIndex, column, columnIndex }) }}</text>
                <text v-else-if="column.cellRender && column.cellRender.name === 'TextEllipsis'" class="text-ellipsis">{{ getTextEllipsis({ type: 'body', row, rowIndex, column, columnIndex }) }}</text>
                <ml-amount-text v-else-if="column.cellRender && column.cellRender.name === 'MlAmountText'" :value="row[column.field]" :simplify="column.cellRender.props ? column.cellRender.props.simplify : false"></ml-amount-text>
                <ml-progress-bar
                  v-else-if="column.cellRender && column.cellRender.name === 'MlProgressBar'"
                  :value="row[column.cellRender.field || column.field]"
                  :title="row[column.cellRender.titleField] || ''"
                  :titleUnderline="column.cellRender.props ? column.cellRender.props.titleUnderline : false"
                  :showNum="column.cellRender.props ? column.cellRender.props.showNum : false"
                  :max="column.cellRender.props && column.cellRender.props.max ? column.cellRender.props.max : 100"
                  :activeColor="getBarActiveColor({ type: 'body', row, rowIndex, column, columnIndex })"
                  :backgroundColor="column.cellRender.props ? column.cellRender.props.backgroundColor : ''">
                </ml-progress-bar>
                <ml-progress-bar
                  v-else-if="column.cellRender && column.cellRender.name === 'MlProgressDiffBar'"
                  :value="row[column.cellRender.field || column.field]"
                  :title="row[column.cellRender.titleField] || ''"
                  :max="row[column.cellRender.maxField] || ''"
                  :titleUnderline="column.cellRender.props ? column.cellRender.props.titleUnderline : false"
                  :showNum="column.cellRender.props ? column.cellRender.props.showNum : false"
                  :activeColor="getBarActiveColor({ type: 'body', row, rowIndex, column, columnIndex }) || '#FA8C16'"
                  :backgroundColor="(column.cellRender.props ? column.cellRender.props.backgroundColor : '') || '#1890FF'">
                </ml-progress-bar>
                <ml-button
                  v-else-if="column.cellRender && column.cellRender.name === 'MlButton'"
                  :type="column.cellRender.props ? column.cellRender.props.type : 'button'"
                  :underline="column.cellRender.props ? column.cellRender.props.underline : false"
                  :status="column.cellRender.props ? column.cellRender.props.status : ''"
                  :content="column.cellRender.props ? column.cellRender.props.content : ''"
                  @click="btnEvent({ type: 'body', row, rowIndex, column, columnIndex })">
                </ml-button>
                <ml-approval-type
                  v-else-if="column.cellRender && column.cellRender.name === 'MlApprovalType'"
                  :value="row[column.cellRender.field || column.field]">
                </ml-approval-type>
                <ml-dict-type
                  v-else-if="column.cellRender && column.cellRender.name === 'MlDictType'"
                  :options="column.cellRender.options || []"
                  :value="row[column.cellRender.field || column.field]">
                </ml-dict-type>
                <text v-else>{{ getCellValue({ type: 'body', row, rowIndex, column, columnIndex }) }}</text>
              </view>
            </view>
          </view>
        </slot>
      </view>
    </view>
    <view v-if="$slots.bottom" class="ml-table-bottom-warpper">
      <slot name="bottom"></slot>
    </view>
  </view>
</template>

<script>
import XEUtils from 'xe-utils'

/**
 * columns: [{ title, field, width: '20%' }]
 */
export default {
  name: 'MlTable',
  props: {
    loading: Boolean,
    className: String,
    border: Boolean,
    height: [Number, String],
    headerData: Array,
    data: Array,
    columns: Array,
    columnConfig: {
      type: Object,
      default: () => ({})
    },
    showMore: Boolean,
    loadStatus: String,
    rowConfig: {
      type: Object,
      default: () => ({})
    },
    sortConfig: {
      type: Object,
      default: () => ({})
    },
    showOverflow: {
      type: Boolean,
      default: true
    },
    headerCellClass: [String, Function],
    headerCellFormat: Function,
    cellClass: [String, Function],
    cellFormat: Function,
    // 仅用于MlProgressBar参数
    barActiveColor: Function
  },
  provide () {
    return {
      mlTable: this
    }
  },
  inject: {
    currVM: {
      from: '$pageVM',
      default: null
    }
  },
  data () {
    return {
      tableColumn: [],
      tableData: []
    }
  },
  computed: {
    rowOpts () {
      return { ...this.rowConfig }
    },
    columnOpts () {
      return { ...this.columnConfig }
    },
    sortOpts () {
      return { ...this.sortConfig }
    },
    tableStyle () {
      return `height:${this.height ? (isNaN(this.height) ? this.height : `${this.height}rpx`) : ''}`
    },
    colMinWidth () {
      return Math.max(20, XEUtils.floor(100 / this.tableColumn.length, 2))
    }
  },
  watch: {
    columns () {
      this.updateColumn()
    },
    data () {
      this.updateData()
    }
  },
  created () {
    this.updateColumn()
    this.updateData()
  },
  methods: {
    updateData () {
      this.tableData = this.data ? this.data.slice(0) : []
    },
    updateColumn () {
      this.tableColumn = this.columns.map(column => {
        let order = null
        if (this.sortOpts.defaultSort && this.sortOpts.defaultSort.field === column.field) {
          order = this.sortOpts.defaultSort.order
        }
        return { ...column, order, colid: XEUtils.uniqueId('col_') }
      })
    },
    getBarActiveColor (params) {
      const { column } = params
      if (this.barActiveColor) {
        if (XEUtils.isFunction(this.barActiveColor)) {
          return this.barActiveColor.call(this.currVM, params)
        }
        return `${this.barActiveColor}`
      }
      return column.cellRender.props ? column.cellRender.props.activeColor : ''
    },
    getHeaderCellClass (params) {
      const { column } = params
      const clss = []
      const showOverflow = XEUtils.isBoolean(column.showOverflow) ? column.showOverflow : this.showOverflow
      if (showOverflow) {
        clss.push('is-ellipsis')
      }
      if (this.headerCellClass) {
        if (XEUtils.isFunction(this.headerCellClass)) {
          clss.push(this.headerCellClass.call(this.currVM, params))
        } else {
          clss.push(`${this.headerCellClass}`)
        }
      }
      return clss.join(' ')
    },
    getHeaderCellValue (params) {
      const { row, column } = params
      const cellValue = row[column.field]
      if (XEUtils.isFunction(this.headerCellFormat)) {
        return this.headerCellFormat.call(this.currVM, { ...params, cellValue })
      }
      return XEUtils.eqNull(cellValue) ? '' : cellValue
    },
    getCellClass (params) {
      const { row, column } = params
      const cellValue = row[column.field]
      const clss = []
      const showOverflow = XEUtils.isBoolean(column.showOverflow) ? column.showOverflow : this.showOverflow
      if (showOverflow) {
        clss.push('is-ellipsis')
      }
      if (this.cellClass) {
        if (XEUtils.isFunction(this.cellClass)) {
          clss.push(this.cellClass.call(this.currVM, { ...params, cellValue }))
        } else {
          clss.push(`${this.cellClass}`)
        }
      }
      return clss.join(' ')
    },
    getCellValue (params) {
      const { row, column } = params
      const cellValue = row[column.field]
      if (this.cellFormat) {
        return this.cellFormat.call(this.currVM, { ...params, cellValue })
      }
      return XEUtils.eqNull(cellValue) ? '' : cellValue
    },
    getTextEllipsis (params) {
      const { column } = params
      const cellText = this.getCellValue(params)
      const maxSize = (column.width ? Math.floor(column.width / 26) : 4) * 2
      if (cellText && cellText.length > maxSize) {
        return `${cellText.substring(0, maxSize - 1)}...`
      }
      return XEUtils.eqNull(cellText) ? '' : cellText
    },
    getColHeight (column) {
      if (this.rowOpts.height) {
        return `${this.rowOpts.height};`
      }
      return this.showOverflow ? '84rpx' : ''
    },
    getColWidth (column) {
      const colWidth = column.width || this.columnOpts.width
      if (colWidth) {
        if (isNaN(colWidth)) {
          return colWidth
        }
        return `${colWidth}rpx`
      }
      return `${this.colMinWidth}%`
    },
    getTableHeight () {
      if (this.height) {
        if (isNaN(this.height)) {
          return this.height
        }
        return `${this.height}rpx`
      }
      return ''
    },
    sort (fieldOrColumn, order) {
      const column = fieldOrColumn ? this.tableColumn.find(item => item.colid === fieldOrColumn.colid || item.field === fieldOrColumn) : null
      if (column) {
        const targetOrder = column.order === order ? null : order
        this.tableColumn.forEach(item => {
          item.order = null
        })
        column.order = targetOrder
        if (column.order) {
          this.tableData = XEUtils.orderBy(this.tableData, { field: column.field, order: column.order })
        } else {
          this.tableData = this.data ? this.data.slice(0) : []
        }
      }
    },
    sortEvent (params, order) {
      const { column } = params
      if (!this.sortOpts.remote) {
        this.sort(column, order)
      }
      this.$emit('sort-change', params)
    },
    chickThEvent (params) {
      const { column } = params
      if (column.sortable) {
        let order
        if (column.order === 'asc') {
          order = 'desc'
        } else if (column.order === 'desc') {
          order = null
        } else {
          order = 'asc'
        }
        this.sort(column, order)
      }
      this.$emit('header-th-click', params)
    },
    cellHeaderTdClick (params) {
      this.$emit('header-cell-click', params)
    },
    cellTdClick (params) {
      const { column } = params
      if (column.cellRender && column.cellRender.name === 'TextEllipsis') {
        uni.showModal({
          title: column.title,
          content: `${this.getCellValue(params)}`,
          showCancel: false
        })
      }
      this.$emit('cell-click', params)
    },
    btnEvent (params) {
      this.$emit('cell-button-click', params)
    },
    updateCellModel ({ field, value }) {
      this.$emit('input', { ...this.value, [field]: value })
    }
  }
}
</script>

<style lang="scss">
.ml-table {
  display: block;
  width: 100%;
  font-size: $ml-base-font-size;
  box-shadow: 0rpx 0rpx 10rpx -3rpx rgba(0, 0, 0, 0.2);
  background: #fff;
  &.is-border {
    .table-th,
    .ml-table-td {
      border-right: 1px solid rgba(126, 134, 142, 0.16);
      &:last-child {
        border-right: 0;
      }
    }
  }
  .ml-table-warpper {
    overflow: auto;
  }
  .table-header {
    position: sticky;
    top: 0;
    z-index: 1;
  }
  .table-body-top {
    background: #fff;
    .ml-table-td {
      height: 84rpx;
    }
  }
  .table-th {
    flex-shrink: 0;
    display: inline-flex;
    height: 88rpx;
    line-height: 88rpx;
    color: #000000;
    text-align: center;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    background: #F5F5F5;
    border-bottom: 1px solid rgba(126, 134, 142, 0.16);
    box-sizing: border-box;
    &.is-sort {
      .th-title {
        padding-right: 26rpx;
      }
    }
    .th-title {
      display: block;
      width: 100%;
      padding: 0 8rpx;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }
  }
  .ml-table-tr {
    display: inline-flex;
    white-space: nowrap;
    width: 100%;
  }
  .table-th {
    position: relative;
    font-size: 28rpx;
    .th-sort {
      position: absolute;
      top: 50%;
      right: 0;
      transform: translateY(-50%);
    }
    .asc-btn,
    .desc-btn {
      height: 18rpx;
      line-height: 18rpx;
      &.active {
        color: #2982ea;
      }
    }
  }
  .ml-table-td {
    flex-shrink: 0;
    display: inline-flex;
    flex-direction: row;
    align-items: center;
    padding: 0 10rpx;
    text-align: center;
    color: rgba(39, 42, 48, 0.65);
    border-bottom: 1px solid rgba(126, 134, 142, 0.16);
    box-sizing: border-box;
    font-size: 24rpx;
    min-height: 84rpx;
  }
  .ml-table-cell {
    display: block;
    width: 100%;
    white-space: normal;
    word-break: break-all;
    &.is-ellipsis {
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }
    &.align-left {
      text-align: left;
    }
    &.align-center {
      text-align: center;
    }
    &.align-right {
      text-align: right;
    }
  }
  .is-underline {
    text-decoration: underline;
  }
  .ml-table-bottom-warpper {
    display: flex;
    flex-direction: column;
    min-height: 70rpx;
    justify-content: center;
    border-top: 1px solid rgba(126, 134, 142, 0.16);
  }
}
</style>
