<template>
  <div
    class="ui-grid"
    :class="['ui-grid--' + type, { 'is-fixed-bottom': fixedBottom, 'is-border': border }]"
    :style="gridStyle"
  >
    <div class="ui-grid__wrap" v-if="ready && showWrap" ref="wrap">
      <div class="ui-grid__header" ref="header">
        <div class="ui-grid__filter-simple">
          <ui-filter ref="filter" :value="filterProps.value" :ready="filterReady" @change="handleFilterChange">
            <ui-filter-item
              class="ui-grid__filter-simple-item"
              v-for="item in simpleFilterItems"
              :key="item.id"
              v-bind="item.props"
            >
              <component :is="handleRenderFilterItem(item)" :data-key="item.data && item.data.key"></component>
            </ui-filter-item>
          </ui-filter>
        </div>
        <div class="ui-grid__sort" v-if="sortProps && sortProps.items && sortProps.items.length">
          <grid-sort
            v-bind="sortProps"
            @change="handleSortChange"
          ></grid-sort>
        </div>
      </div>
    </div>
    <div class="ui-grid__table" :style="tableStyle">
      <div class="ui-grid__batch" ref="batch">
        <transition @before-enter="handleBatchBeforeEnter" @after-leave="handleBatchAfterLeave">
            <div class="ui-grid__batch-inner" v-if="batchToolbarProps.items && !!selecteds.length">
              <grid-batch-toolbar
                :data="data"
                :selecteds="selecteds"
                v-bind="batchToolbarProps"
                @toggle-selection="handleToggleSelection"
                @batch-success="handleBatchSuccess"
              ></grid-batch-toolbar>
            </div>
        </transition>
      </div>
      <grid-table
        ref="table"
        :show-index="showIndex"
        :border="border"
        :multiple="multiple"
        :height="tableHeight"
        :max-height="maxHeight"
        :data="tableData"
        :total="tableTotal"
        :page-index="tablePageIndex"
        :page-size="tablePageSize"
        :empty-text="emptyText"
        :fixed-row-toolbar="fixedRowToolbar"
        :show-pagination="showPagination"
        @page-change="handlePageChange"
      >
        <slot></slot>
        <grid-column
          label="操作"
          fixed="right"
          :width="rowToolbarWidth"
          v-if="rowToolbar && rowToolbar.length"
        >
          <template slot-scope="{ row, $index }">
            <grid-row-toolbar :row="row" :row-index="$index" :items="rowToolbar"></grid-row-toolbar>
          </template>
        </grid-column>
      </grid-table>
      <div
        class="ui-grid__loading"
        v-if="tableLoading"
      >
        <ui-loading :visible="tableLoading" type="primary" size="medium"></ui-loading>
      </div>
    </div>
    <div class="ui-grid__pagination" ref="pagination" v-if="showPagination">
      <div class="ui-grid__pagination-inner" :style="fixedBottomStyle">
        <ui-pagination
          :page-index="tablePageIndex"
          :page-size="tablePageSize"
          :total="tableTotal"
          :title="title"
          @page-change="handlePageChange"
        >
          <template slot="addon">
            <span
              class="ui-grid__refresh"
              :class="{'is-loading': tableLoading}"
              title="刷新"
              @click="handleRefreshClick"
            ></span>
          </template>
        </ui-pagination>
      </div>
    </div>
  </div>
</template>
<script>
import { Filter, FilterItem } from '@huntianning/components/packages/filter'
import GridTable from './grid-table'
import GridColumn from './grid-column'
import GridRowToolbar from './grid-toolbar/grid-row-toolbar'
import GridGlobalToolbar from './grid-toolbar/grid-global-toolbar'
import GridBatchToolbar from './grid-toolbar/grid-batch-toolbar'
import GridMoreToolbar from './grid-toolbar/grid-more-toolbar'
import { GridSort } from './grid-sort'
import Pagination from '@huntianning/components/packages/pagination'
import Loading from '@huntianning/components/packages/loading'

export default {
  name: 'UiGrid',
  inject: {
    UiPage: {
      default: null
    },
    UiDialog: {
      default: null
    }
  },
  provide() {
    return {
      UiGrid: this
    }
  },
  components: {
    UiLoading: Loading,
    UiFilter: Filter,
    UiFilterItem: FilterItem,
    UiPagination: Pagination,
    GridSort,
    GridColumn,
    GridRowToolbar,
    GridGlobalToolbar,
    GridBatchToolbar,
    GridMoreToolbar,
    GridTable
  },
  props: {
    height: {
      type: String,
      default: 'full'
    },
    maxHeight: {
      type: String
    },
    multiple: {
      type: Boolean,
      default: false
    },
    emptyText: {
      type: String
    },
    data: {
      type: Array
    },
    rowToolbar: {
      type: Array
    },
    rowToolbarWidth: {
      type: [String, Number],
      default: '120px'
    },
    fixedRowToolbar: {
      type: Boolean
    },
    loading: {
      type: Boolean,
      default: false
    },
    queryMethod: {
      type: Function
    },
    showPagination: {
      type: Boolean,
      default: true
    },
    showIndex: {
      type: Boolean
    },
    title: {
      type: String
    },
    total: {
      type: Number
    },
    border: {
      type: Boolean,
      default: false
    },
    type: {
      type: String,
      default: 'split',
      validator(value) {
        return ['split', 'default'].includes(value)
      }
    },
    autoLoad: {
      type: Boolean,
      default: true
    },
    /**
     * 固定底部分页栏在页面底部
    */
    fixedBottom: {
      type: Boolean,
      default: false
    }
  },
  data() {
    return {
      ready: false,
      filterReady: false,
      selecteds: [],
      globalToolbarProps: {},
      batchToolbarProps: {},
      moreToolbarProps: {},
      sortProps: {
        items: [],
        value: null
      },
      filterProps: {
        items: [],
        value: null
      },
      showColumnSetting: false,
      tablePageIndex: 1,
      tablePageSize: 30,
      tableLoading: this.loading,
      tableTotal: 0,
      tableData: this.data,
      tableStyle: null,
      fixedBottomStyle: null
    }
  },
  computed: {
    showWrap() {
      if (this.globalToolbarProps && this.globalToolbarProps.items && this.globalToolbarProps.items.length) {
        return true
      }
      if (this.moreToolbarProps && this.moreToolbarProps.items && this.moreToolbarProps.items.length) {
        return true
      }
      if (this.simpleFilterItems && this.simpleFilterItems.length) {
        return true
      }
      return false
    },
    gridStyle() {
      const style = {}
      if (this.height && this.height !== 'auto') {
        style.height = this.height
      }
      return style
    },
    simpleFilterItems() {
      if (!this.filterProps || !this.filterProps.items) {
        return
      }
      return (this.filterProps.items || []).filter(v => v.type !== 'advance')
    },
    innerShowSort() {
      return !!(this.sortProps && this.sortProps.items && this.sortProps.items.length)
    },
    tableHeight() {
      if (this.height !== 'auto') {
        return '100%'
      }
      return this.height
    },
    bindUrl() {
      if (this.UiDialog) {
        return false
      }
      return this.$refs.filter && this.$refs.filter.bindUrl
    }
  },
  watch: {
    data(data) {
      this.tableData = data
    },
    tableData() {
      this.$nextTick(() => {
        this.$emit('load')
      })
    },
    loading(val) {
      this.tableLoading = val
    }
  },
  created() {
    this.$on('@table-selection-change', this.handleSelectionChange)
    this.$on('@table-select-change', this.handleSelectChange)
    this.$on('@table-select-all-change', this.handleSelectAllChange)
    this.$on('@table-current-change', this.handleCurrentChange)
    if (this.$route.query && this.$route.query.pageindex) {
      this.tablePageIndex = Number(this.$route.query.pageindex)
    }
  },
  mounted() {
    window.addEventListener('resize', this.handleWindowResize)
    this.ready = true
    this.$nextTick(() => {
      this.filterReady = true
      // 稍微晚一点执行，页面可能还需要加载一些数据
      this.loadTimer = setTimeout(() => {
        if (this.autoLoad) {
          this.loadData()
        }
        this.doLayout()
      })
    })
    this.refreshBottomStyle()
  },
  methods: {
    handleSortChange({ value }) {
      this.sortProps.value = value
      this.$emit('sort-change', {
        value
      })
      this.loadData()
    },
    handleRenderFilterItem(item) {
      return {
        functional: true,
        render: (h, c) => {
          return item.render(h, c)
        }
      }
    },
    handleWindowResize() {
      clearTimeout(this.timer)
      this.timer = setTimeout(() => {
        this.doLayout()
      }, 100)
    },
    handleToggleSelection() {
      this.$emit('@grid-toggle-selection')
    },
    handleBatchSuccess() {
      this.loadData()
    },
    handleSelectionChange(e) {
      this.selecteds = e || []
      this.$emit('selection-change', e)
    },
    handleSelectChange(e) {
      this.$emit('select-change', e)
    },
    handleSelectAllChange(e) {
      this.$emit('select-all-change', e)
    },
    handleCurrentChange(e) {
      this.$emit('current-change', e)
    },
    handlePageChange(e) {
      this.tablePageSize = e.pageSize
      this.tablePageIndex = e.pageIndex
      if (this.bindUrl) {
        this.$router.push({
          path: this.$route.path,
          query: Object.assign({}, this.$route.query, {
            pageindex: e.pageIndex
          })
        })
      }
      this.loadData()
    },
    handleFilterChange() {
      if (!this.autoLoad) {
        return
      }
      this.reload()
    },
    handleRefreshClick() {
      if (this.tableLoading) {
        return
      }
      this.loadData()
    },
    getFilters() {
      if (this.$refs.filter) {
        return this.$refs.filter.getFilters()
      }
      return {}
    },
    getSort() {
      let sort = null
      if (this.sortProps && this.sortProps.value) {
        sort = {
          [this.sortProps.value.prop]: this.sortProps.value.order
        }
      }
      return sort
    },
    reload() {
      this.tablePageIndex = 1
      return this.loadData()
    },
    loadData() {
      return new Promise((resolve, reject) => {
        if (typeof this.queryMethod === 'function') {
          let ps = {}
          const fs = this.getFilters()
          if (this.showPagination) {
            ps = {
              pageNo: this.tablePageIndex,
              pageSize: this.tablePageSize
            }
          }
          const rs = this.queryMethod({
            filter: fs,
            page: ps,
            sort: this.getSort()
          })
          if (rs && rs.then) {
            this.tableLoading = true
            rs.then((data) => {
              this.tableLoading = false
              this._setData(data)
              resolve()
            }, () => {
              this.tableLoading = false
              reject()
            })
          } else {
            this._setData(rs)
          }
        } else {
          resolve()
        }
      })
    },
    _setData(result) {
      if (this.showPagination) {
        let total = 0
        let data = []
        if (result) {
          total = parseInt(result.total) || 0
          data = result.records || []
        }
        this.tableData = data
        this.tableTotal = total
      } else {
        this.tableData = result || []
      }
      this.$nextTick(() => {
        this.doLayout()
      })
      this._timer = setTimeout(() => {
        this.$emit('load')
      })
    },
    _getParentHeight() {
      let height = 0
      if (this.height === 'full' && this.UiPage && !this.UiDialog) {
        height = this.UiPage.getMainHeight()
      } else {
        const $parent = this.$el.parentElement
        let parentHeight = $parent.clientHeight
        const parentPaddingTop = parseInt(window.getComputedStyle($parent).paddingTop)
        const parentPaddingBottom = parseInt(window.getComputedStyle($parent).paddingBottom)
        if (!isNaN(parentPaddingTop)) {
          parentHeight -= parentPaddingTop
        }
        if (!isNaN(parentPaddingBottom)) {
          parentHeight -= parentPaddingBottom
        }
        height = parentHeight
      }
      return height
    },
    handleBatchBeforeEnter() {
      if (this.$refs.batch) {
        this.$refs.batch.style.display = 'block'
      }
    },
    handleBatchAfterLeave() {
      if (this.$refs.batch) {
        this.$refs.batch.style.display = 'none'
      }
    },
    doLayout() {
      const style = {}
      if (this.height === 'auto') {
        if (this.loading || (!this.tableData || !this.tableData.length)) {
          style.minHeight = '320px'
        }
      } else {
        let minusHeight = 0
        if (this.$refs.wrap) {
          minusHeight += this.$refs.wrap.clientHeight
        }
        if (this.$refs.pagination) {
          minusHeight += this.$refs.pagination.clientHeight
        }
        if (this.UiPage) {
          const parentHeight = this._getParentHeight()
          if (this.height && this.height !== 'auto') {
            style.height = parentHeight - minusHeight + 'px'
          } else {
            style.minHeight = parentHeight - minusHeight + 'px'
          }
        } else {
          if (this.height && (this.height.indexOf('px') >= 0 || this.height.indexOf('%') >= 0)) {
            style.height = this.$el.clientHeight - minusHeight + 'px'
          }
        }
      }
      this.tableStyle = style
      this.$nextTick(() => {
        this.$refs.table && this.$refs.table.doLayout()
        this.refreshBottomStyle()
      })
    },
    setTableData(callback) {
      callback && callback(this.tableData)
    },
    toggleRowSelection(row, isSelected) {
      this.$refs.table.toggleRowSelection(row, isSelected)
    },
    setCurrentRow(row) {
      this.$refs.table.setCurrentRow(row)
    },
    refreshBottomStyle() {
      if (!this.fixedBottom) {
        return
      }
      const paginationRect = this.$refs.pagination.getBoundingClientRect()
      this.fixedBottomStyle = {
        width: paginationRect.width + 'px',
        left: paginationRect.left + 'px'
      }
    }
  },
  beforeDestroy() {
    clearTimeout(this.timer)
    clearTimeout(this.loadTimer)
    clearTimeout(this._timer)
    window.removeEventListener('resize', this.handleWindowResize)
  }
}
</script>
<style lang="less">
.ui-grid{
  position: relative;
  &.is-fixed-bottom {
    .ui-grid__pagination{
      min-height: 46px;
    }
    .ui-grid__pagination-inner{
      position: fixed;
      bottom: 0;
      left: 0;
      width: 100%;
      box-sizing: border-box;
      &:before{
        content: '';
        position: absolute;
        top: 0;
        bottom: 0;
        left: -2px;
        width: 4px;
        background-color: #fff;
      }
      &:after {
        content: '';
        position: absolute;
        top: 0;
        bottom: 0;
        right: -2px;
        width: 4px;
        background-color: #fff;
      }
    }
  }
  &.is-border {

  }
}
.ui-grid--split{
  .ui-grid__wrap:after {
    content: '';
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    height: 12px;
    background-color: #f3f4f5;
  }
  .ui-grid__header{
    background-color: #FCFDFD;
    padding-left: 24px;
    padding-right: 24px;
    padding-top: 4px;
    padding-bottom: 4px;
    box-shadow: 0px 2px 4px 0px rgba(218, 218, 218, 0.5);
  }
}
.ui-grid__wrap {
  padding-bottom: 12px;
  position: relative;
}
.ui-grid__header{
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  align-items: flex-start;
  min-height: 28px;
  position: relative;
  z-index: 3;
}
.ui-grid__sort{
  padding-top: 4px;
  padding-bottom: 4px;
}
.ui-grid__filter-simple{
  display: flex;
  flex-direction: row;
  align-items: center;
  flex: 1;
  padding-right: 24px;
}
.ui-grid__filter-simple-item{
  vertical-align: top;
  margin-right: 12px;
}
.ui-grid__pagination-inner{
  padding: 9px 24px;
  background-color: #fff;
  box-shadow: 0px -1px 4px 0px rgba(232, 230, 230, 0.5);
  position: relative;
  z-index: 3;
}
.ui-grid-global-toolbar{
  .ui-toolbar-item{
    line-height: 1;
  }
}
.ui-grid__more-toolbar{
  display: inline-block;
  margin-left: 6px;
}
.ui-grid__batch{
  position: absolute;
  left: 1px;
  right: 1px;
  top: 1px;
  height: 46px;
  overflow: hidden;
  display: none;
  z-index: 10;
}
.ui-grid__batch-inner{
  position: absolute;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding-right: 6px;
  padding-left: 6px;
  background-color: #fff;
  left: 0;
  top: 0;
  right: 0;
  height: 100%;
  transform: translateY(0);
}
.ui-grid__batch-inner.v-enter{
  transform: translateY(-30px);
}
.ui-grid__batch-inner.v-enter-active{
  transition: transform .2s;
}
.ui-grid__batch-inner.v-enter-to{
  transform: translateY(1px);
}
.ui-grid__batch-inner.v-leave{
  transform: translateY(1px);
}
.ui-grid__batch-inner.v-leave-active{
  transition: transform .2s;
}
.ui-grid__batch-inner.v-leave-to{
  transform: translateY(-42px);
}
.ui-grid__table{
  position: relative;
}
.ui-grid__loading{
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 100;
}
@keyframes grid-refreshing{
  0% {
    transform: rotate(0);
  }
  100% {
    transform: rotate(1turn);
  }
}
.ui-grid__refresh{
  margin-left: 12px;
  width: 28px;
  height: 28px;
  line-height: 28px;
  cursor: pointer;
  border-radius: 4px;
  transition: all .25s;
  text-align: center;
  &:hover {
    background-color: #f3f3f3;
  }
  &:after {
    content: '';
    width: 18px;
    height: 18px;
    display: inline-block;
    vertical-align: top;
    margin-top: 5px;
    background: url('./icons/refresh.svg') no-repeat 50% 50%;
    background-size: 16px;
  }
  &.is-loading {
    &:after {
      animation: grid-refreshing 1s linear infinite;
    }
  }
}
</style>
