<template>
  <!-- 公共组件表格 -->
  <div>
    <slot name="out"></slot>
    <div class="tools d-flex justify-end" :class="{'mb-3': isMb}">
      <slot name="navBtn"></slot>
      <div v-if="isTools">
        <v-btn class="ml-2 elevation-5 buttons" color="primary" rounded @click="$emit('add')">
          <v-icon left>mdi-plus</v-icon>
          新增
        </v-btn>
        <v-btn v-if="canDelete" class="ml-2 elevation-5 buttons" color="error" rounded @click="deleteSelect">
          <v-icon left>mdi-trash-can</v-icon>
          删除
        </v-btn>
        <slot name="addToTools"></slot>
      </div>
      <div v-if="isHeaders" class="ml-2" :style="`width: ${headerWidth}px`">
        <v-select
          class="mt-0 pt-0"
          v-model="selectHeader"
          :items="selectItems"
          hide-details
          label="自定义头部"
          placeholder="自定义头部"
          height="36"
          multiple
          @change="handleSelectChange"
        >
          <template v-slot:selection="{ item, index }">
            <v-chip v-if="index === 0">
              <span>{{ item }}</span>
            </v-chip>
            <span
              v-if="index === 1"
              class="grey--text text-caption"
            >
              (+{{ selectHeader.length - 1 }} others)
            </span>
          </template>
          <template v-slot:prepend-item>
            <v-list-item
              ripple
              @mousedown.prevent
              @click="toggle"
            >
              <v-list-item-action>
                <v-icon :color="selectHeader.length > 0 ? 'indigo darken-4' : ''">
                  {{ icon }}
                </v-icon>
              </v-list-item-action>
              <v-list-item-content>
                <v-list-item-title>
                  全选
                </v-list-item-title>
              </v-list-item-content>
            </v-list-item>
            <v-divider class="mt-2"></v-divider>
          </template>
        </v-select>
      </div>
    </div>
    <v-data-table
      v-model="selected"
      ref="table"
      :class="`elevation-${elevation} tableColor ${noRadius ? 'noRadius' : ''}`"
      :headers="useHeaders"
      :items="items"
      :item-key="itemKey"
      :show-select="showSelect"
      :single-select="singleSelect"
      :loading="loading"
      hide-default-footer
      loading-text="Loading... Please wait"
      :show-expand="showExpand"
      :single-expand="singleExpand"
      :calculate-widths="true"
      :items-per-page="itemsPerPage"
      fixed-header
      :height="height"
      :disable-sort="disableSort"
      :no-data-text="noDataText"
      :multi-sort="multiSort"
      :hide-default-header="hideDefaultHeader"
      :search="search"
      :server-items-length="total"
      :options.sync="options"
      @item-selected="handleSelected"
      @toggle-select-all="handleSelected"
      @item-expanded="handleExpanded"
      v-on="$listeners"
      :expanded="expandedRows"
    >
      <template
        v-for="name in itemSlot"
        v-slot:[`item.${name}`]="slotProps"
      >
        <!-- {{ Object.keys(slotProps) }} -->
        <slot :name="name" v-bind="slotProps"></slot>
      </template>
      <template
        v-for="name in headerSlot"
        v-slot:[`${name}`]="slotProps"
      >
        <!-- {{ Object.keys(slotProps) }} -->
        <slot :name="name" v-bind="slotProps"></slot>
      </template>
      <template v-if="!Object.keys($scopedSlots).includes('actions')" v-slot:[`item.actions`]="{ item }">
          <td>
            <v-btn
                text
                color="primary"
                @click="edit(item)"
            >
                编辑
            </v-btn>
            <v-btn
                text
                color="error"
                @click="del(item)"
            >
                删除
            </v-btn>
          </td>
      </template>
      <template v-if="showPage && total > 0" v-slot:footer>
        <v-divider></v-divider>
        <pagination :total="total" :page="pageInfo.current" :totalVisible="totalVisible" :limit="pageInfo.size" @handleChange="pageHandleChange" />
      </template>
      <template v-slot:expanded-item="{ headers, item }">
        <slot name="expanded-item" :item="item" :headers="headers"></slot>
      </template>
      <template v-if="$scopedSlots.body" v-slot:body="data">
        <slot name="body" v-bind="data"></slot>
      </template>
      <template v-slot:header="{ props, on }">
        <slot name="header" :props="props" :on="on"></slot>
      </template>
      <template v-if="$scopedSlots['body.append']" v-slot:[`body.append`]="item">
        <slot name="body.append" v-bind="item"></slot>
      </template>
    </v-data-table>
    <slot></slot>
  </div>
</template>

<script>
import Pagination from '@/components/Pagination'
import { camelToUnderscore } from '@/utils'
/**
 * header {text,align,sortable,value}
 */
export default {
  name: 'list-table',
  components: { Pagination },
  props: {
    search: {
      type: String,
      default: ''
    },
    // 是否显示顶部间距
    isMb: {
      type: Boolean,
      default: true
    },
    elevation: {
      type: [Number, String],
      default: 5
    },
    itemKey: {
      type: String,
      default: 'id'
    },
    showSelect: {
      type: Boolean,
      default: true
    },
    singleSelect: {
      type: Boolean,
      default: false
    },
    autoAction: {
      type: Boolean,
      default: true
    },
    disableSort: {
      type: Boolean,
      default: false
    },
    showExpand: {
      type: Boolean,
      default: false
    },
    singleExpand: {
      type: Boolean,
      default: false
    },
    // 默认单个排序
    multiSort: {
      type: Boolean,
      default: false
    },
    // 展示列表所有数据
    itemsPerPage: {
      type: Number,
      default: -1
    },
    showPage: {
      type: Boolean,
      default: true
    },
    loading: {
      type: Boolean,
      default: true
    },
    headers: {
      type: Array,
      default: () => []
    },
    items: {
      type: Array,
      default: () => []
    },
    total: {
      type: [String, Number],
      default: -1
    },
    pageInfo: {
      type: Object,
      default: () => ({
        size: 10
      })
    },
    isTools: {
      type: Boolean,
      default: true
    },
    canDelete: {
      type: Boolean,
      default: true
    },
    isHeaders: {
      type: Boolean,
      default: false
    },
    height: {
      type: [String, Number],
      default: ''
    },
    headerWidth: {
      type: [Number, String],
      default: 200
    },
    // 默认展示的头部 ['', '', '']
    defaultHeader: {
      type: Array,
      default: () => []
    },
    defaultHeaderShow: { // 默认显示头部个数
      type: Number,
      default: 10
    },
    hideDefaultHeader: {
      type: Boolean,
      default: false
    },
    // 选中返回整个item
    selectItem: {
      type: Boolean,
      default: false
    },
    noRadius: {
      type: Boolean,
      default: false
    },
    // 选中的项-回显
    value: {
      type: Array,
      default: () => []
    },
    noDataText: {
      type: String,
      default: 'No data available'
    },
    totalVisible: {
      type: Number,
      default: 7
    }
  },
  watch: {
    sortChange () {
      this.handleChangeOptions()
    },
    headers (val) {
      this.headersFinish()
    },
    value: {
      handler (val) {
        this.selected = [...val]
        this.ids = [...val]
      },
      deep: true
    }
  },
  computed: {
    itemSlot () {
      return Object.keys(this.$scopedSlots).filter(key => {
        const data = key.split('.')
        return data.length === 1
      })
    },
    headerSlot () {
      return Object.keys(this.$scopedSlots).filter(key => {
        const data = key.split('.')
        return data.length === 2 && data[0] === 'header'
      })
    },
    likesAllFruit () {
      return this.selectHeader.length === this.selectItems.length
    },
    likesSomeFruit () {
      return this.selectHeader.length > 0 && !this.likesAllFruit
    },
    icon () {
      if (this.likesAllFruit) return 'mdi-close-box'
      if (this.likesSomeFruit) return 'mdi-minus-box'
      return 'mdi-checkbox-blank-outline'
    },
    useHeaders () {
      return this.backupHeaders.map(e => {
        if (!e.class) {
          e.class = 'custom-header-class'
          return e
        }

        if (typeof e.class === 'string') {
          e.class += ' custom-header-class'
        } else {
          e.class.push('custom-header-class')
        }
        return e
      })
    },
    sortChange () {
      return this.options.sortDesc.toString() + this.options.sortBy.toString()
    }
  },
  data () {
    return {
      expandedRows: [], // 展开项
      selected: [],
      // slotHeaders: [],
      ids: [],
      selectHeader: [], // 选择头部
      selectItems: [], // 过滤展示头部
      backupHeaders: [],
      options: {
        page: 1,
        itemsPerPage: -1,
        sortBy: [],
        sortDesc: [],
        groupBy: [],
        groupDesc: [],
        mustSort: false,
        multiSort: false
      },
      // sortBy: [],
      // sortDesc: [],
      headerCountShow: this.defaultHeaderShow
    }
  },
  created () {
    this.headersFinish()
  },
  mounted () {
    // this.selected = [...this.value]
    // this.ids = [...this.value]
  },
  methods: {
    headersFinish () {
      const list = this.headers.map(e => e.text)
      if (this.defaultHeader.length) {
        this.selectHeader = this.defaultHeader
      } else {
        this.selectHeader = list.length > this.headerCountShow ? list.splice(0, this.headerCountShow) : list
      }
      this.selectItems = this.headers.map(e => e.text)
      this.initHeaders()
    },
    toggle () {
      this.$nextTick(() => {
        if (this.likesAllFruit) {
          this.selectHeader = []
        } else {
          this.selectHeader = this.selectItems.slice()
        }
        this.handleSelectChange()
      })
    },
    initHeaders () {
      if (this.isHeaders) {
        this.backupHeaders = this.headers.filter(e => {
          if (e.value === 'actions') return true
          return this.selectHeader.includes(e.text)
        })
      } else this.backupHeaders = this.headers
      // 自动锁定排序
      this.backupHeaders.forEach(item => {
        if (item.value === 'actions') {
          item.sortable = false
          return
        }
        item.sort = (a, b) => {
          return true
        }
      })
    },
    pageHandleChange (v) {
      this.$emit('pageHandleChange', v)
    },
    edit (item) {
      this.$emit('edit', item)
    },
    del (item) {
      this.$emit('delete', item.id, item)
    },
    handleSelected (data) {
      if (this.selectItem) return this.handleSelectedItem(data)
      if (Array.isArray(data.items)) this.ids = data.value ? data.items.map(item => item?.[this.itemKey]) : []
      else {
        if (data.value) this.ids.push(data.item[[this.itemKey]])
        else this.ids.splice(this.ids.findIndex(key => key.id === data.item[this.itemKey]))
      }
      this.$emit('selected', this.ids)
    },
    // 选中返回整个item
    handleSelectedItem (data) {
      if (Array.isArray(data.items)) this.ids = data.value ? data.items.map(item => item) : []
      else {
        if (data.value) this.ids.push(data.item)
        else this.ids.splice(this.ids.findIndex(key => key[this.itemKey] === data.item[this.itemKey]), 1)
      }
      this.$emit('selected', this.ids)
    },
    deleteSelect () {
      this.$emit('delete', this.ids)
    },
    handleExpanded (item, value) {
      if (!this.showExpand) return
      this.$emit('expand', item, value)
    },
    handleSelectChange () {
      this.headerCountShow = this.selectHeader.length
      this.initHeaders()
      this.$emit('headerChange', this.selectHeader)
    },
    // handleSortDesc (val) {
    //   console.log('sort', val)
    //   const obj = this.sortDesc.map((item, index) => {
    //     return {
    //       column: '`' + camelToUnderscore(this.sortBy[index]) + '`',
    //       asc: !item
    //     }
    //   })
    //   this.$emit('sort', obj)
    // },
    handleChangeOptions () {
      if (!this.options.sortDesc || !this.options.sortDesc.length) {
        this.$emit('sort', [])
        return
      }
      if (!this.options.sortBy || !this.options.sortBy.length) {
        this.$emit('sort', [])
        return
      }
      const obj = this.options.sortDesc.map((item, index) => {
        return {
          column: '`' + camelToUnderscore(this.options.sortBy[index]) + '`',
          asc: !item
        }
      })
      this.$emit('sort', obj)
    },
    // 清空列表中的展开项
    closeAllRows () {
      this.expandedRows = []
    }
  }
}
</script>

<style lang="scss" scoped>
::v-deep {
  .v-data-table > .v-data-table__wrapper > table > thead > tr > th:first-of-type,
  .v-data-table > .v-data-table__wrapper > table > tbody > tr > td:first-of-type
  {
    // padding: 0 10px;
    padding-left: 10px;
  }
  .v-data-table > .v-data-table__wrapper > table > tbody > tr > td,
  .v-data-table > .v-data-table__wrapper > table > tbody > tr > th,
  .v-data-table > .v-data-table__wrapper > table > thead > tr > td,
  .v-data-table > .v-data-table__wrapper > table > thead > tr > th,
  .v-data-table > .v-data-table__wrapper > table > tfoot > tr > td,
  .v-data-table > .v-data-table__wrapper > table > tfoot > tr > th {
    padding: 0 5px;
  }
  // 鼠标经过颜色
  .theme--light.v-data-table > .v-data-table__wrapper > table > tbody > tr:hover:not(.v-data-table__expanded__content):not(.v-data-table__empty-wrapper) {
    background-color: rgba(18, 125, 212, 0.1);
  }
  // 表头字体调大
  // .v-data-table > .v-data-table__wrapper > table > thead > tr > th {
  //   font-size: 18px;
  // }
  .v-data-table > .v-data-table__wrapper > table > thead > tr > th {
    // background-color: rgba(18, 125, 212, 0.1);
    background-color: #e7f2fb;
    font-size: 14px;
    font-weight: bolder !important;
  }
}
.noRadius {
  border-radius: 0;
}
::v-deep .custom-header-class {
  white-space: nowrap;
  // background: #EEE !important;
}
// ::v-deep .v-data-table--fixed-header > .v-data-table__wrapper {
//   overflow: visible !important;
// }
::v-deep .v-input--selection-controls__input .v-icon {
  color: #1976d2 !important;
}
</style>
