<!--
 * @Author: fxs bjnsfxs@163.com
 * @Date: 2024-08-20 18:16:18
 * @LastEditors: fxs bjnsfxs@163.com
 * @LastEditTime: 2024-09-11 17:05:33
 * @FilePath: \Game-Backstage-Management-System\src\components\Table.vue
 * @Description: 
 * 
-->
<script setup lang="ts">
import type { PropsParams, TablePaginationSetting } from '@/types/table'
import type { ReqConfig } from '@/types/dataAnalysis'
import { FilterType, FieldSpecialEffectType } from '@/types/table'
import { initLoadResouce } from '@/utils/resource'

import { computed, onMounted, reactive, ref, toRaw, watch } from 'vue'
import { useTable } from '@/hooks/useTable'

import FilterPopover from './toolsBtn/FilterPopover.vue'
import RegreshBtn from './toolsBtn/RegreshBtn.vue'
import { useRequest } from '@/hooks/useRequest'

import type { FormInstance } from 'element-plus'
import axiosInstance from '@/utils/axios/axiosInstance'
import { clearReactiveData } from '@/utils/common'

const { analysisResCode } = useRequest()

// 表格工具图标大小
const toolsIconSize = ref(25)

// 查询表单
const queryFormRef = ref<FormInstance>()

// 传过来的配置
const props = withDefaults(defineProps<PropsParams>(), {
  needRowindex: true,
  needAverage: false,
  needLeftTools: false,
  needRightTools: false,
  openFilterQuery: false,
  openPageQuery: false,
  needUpload: false,
  needDownLoad: false
})

// 父组件触发的方法
const emits = defineEmits(['addNewItem', 'upload', 'downLoad', 'loadSuccess'])

// 加载动画
const loading = ref(false)

// 表格数据
const tableData: Array<any> = reactive([])

// 查询表单的数据
const queryFormData = reactive<any>({})

// 分页数据
const paginationConfig2 = reactive<TablePaginationSetting>({
  currentPage: 0,
  limit: 0,
  total: 0,
  pagesizeList: []
})

// 请求配置
const reqconfig = reactive<ReqConfig>({
  url: '',
  otherOptions: {}
})

// 资源的加载路径
const resourceInfo: Record<string, string> = {
  defaultHead: `/img/default/defaultHead.png`
}

// 使用blob的资源路径信息
const blobUrlInfo = reactive<Record<string, string>>({})

// 一些公用方法
const { getTableData } = useTable(tableData, paginationConfig2)

// 没有开启分页查询的时候使用的数据
const tableDataNoPaging = computed(() => {
  let curPage = paginationConfig2.currentPage
  let limit = paginationConfig2.limit
  let begin = curPage * limit - limit
  //这里不减一是因为，slice方法裁切是左闭右开数组
  let end = curPage * limit
  return tableData.slice(begin, end)
})

// 所有类型为input的表单控件信息
const inputFieldsList = computed(() => {
  return props.queryInfo?.filter((item) => item.type === FilterType.INPUT)
})

// 所有类型为select的表单控件信息
const selectFieldsList = computed(() => {
  return props.queryInfo?.filter((item) => {
    return item.type === FilterType.SELECT
  })
})

// 所有类型为date的表单控件信息
const dateFieldsList = computed(() => {
  return props.queryInfo?.filter((item) => item.type === FilterType.DATE)
})

// 计算行号
const computedRowIndex = (index: number) => {
  return (paginationConfig2.currentPage - 1) * paginationConfig2.limit + index + 1
}

// 改变页码
const handleCurrentChange = (val: number) => {
  paginationConfig2.currentPage = val
}

// 改变每页大小
const handleSizeChange = (val: number) => {
  paginationConfig2.limit = val
}

/**
 * @description: 获取数据，如果没有直接传入数据，则去请求数据，有则直接用
 * @return {*}
 */
const getData = () => {
  return new Promise((resolve, reject) => {
    if (props.dataList) {
      tableData.splice(0, tableData.length, ...props.dataList)
      paginationConfig2.total = props.paginationConfig.total
      loading.value = false
      emits('loadSuccess', tableData)
      resolve(true)
    } else {
      loading.value = true
      if (props.requestConfig) {
        if (props.openPageQuery) {
          // 如果开启了分页查询，那么要计算出需要展示的页码位置所对应的偏移量
          // 同时要将查询的条数改为对应的用户选择的展示条数
          reqconfig.otherOptions.offset =
            (paginationConfig2.currentPage - 1) * paginationConfig2.limit
          reqconfig.otherOptions.limit = paginationConfig2.limit
        }

        // 查询时要根据是否开启分页查询传入对应参数
        getTableData(reqconfig.url, reqconfig.otherOptions, props.openPageQuery)
          .then(() => {
            emits('loadSuccess', tableData)
            resolve(true)
          })
          .catch((err) => {
            console.log(err)

            reject(err)
          })
          .finally(() => {
            loading.value = false
          })
      } else {
        loading.value = false

        throw new Error('no match requestConfig')
      }
    }

    if (tableData.length) {
      if (props.needAverage) {
        let rowData: any = {}
        let oldList: Array<any> = JSON.parse(JSON.stringify(tableData))
        Object.values(props.tableFieldsInfo).map((item, index) => {
          let sum = oldList
            .map((item) => item.count)
            .reduce((accumulator, currentValue) => accumulator + currentValue, 0)
          let averageList = oldList
            .map((val) => val[item.name])
            .filter((item) => item !== undefined)
          if (index === 0) rowData[item.name] = '均值'
          else if (item.name === 'count') rowData[item.name] = sum
          else {
            let num =
              averageList.reduce((accumulator, currentValue) => accumulator + currentValue, 0) /
              averageList.length

            rowData[item.name] = isNaN(num) ? 0 : num.toFixed(2)
          }
        })
        insertRow(0, rowData)
      }
    }
  })
}

// 清空表格数据
const resetTableData = () => {
  tableData.splice(0, tableData.length)
}

// 按条件查询
const queryTableData = () => {
  if (props.requestConfig) {
    reqconfig.otherOptions = { ...props.requestConfig.otherOptions, ...queryFormData }
    getData()
  } else {
    throw new Error('no match requestConfig')
  }
}

/**
 * @description: 重置整个查询表单，重置后，再请求一次全部表格
 * @param {*} formEl  表单对象
 * @return {*}
 */
const resetQueryForm = (formEl: FormInstance | undefined) => {
  if (!formEl) return
  clearReactiveData(queryFormData)
  queryTableData()
}

/**
 * @description: 在获取完数据后，插入均值行
 * @param {*} start 插入的位置
 * @param {*} rowData 插入的数据
 * @return {*}
 */
const insertRow = (start: number, rowData: any) => {
  if (props.openPageQuery) {
    tableData[start].splice(0, 0, rowData)
  } else {
    tableData.splice(start, 0, rowData)
  }
}

/**
 * @description: 根据计算出来的值去返回对应的颜色深度
 * @return {*}
 */
const getDecimalFromRange = (number: number) => {
  if (number === null || number === undefined) return 0
  if (number < 25) return 0.25
  else if (number < 50) return 0.5
  else if (number < 75) return 0.75
  else return 1.0 // 如果number >= 75，则直接返回1.00
}

/**
 * @description: 单独处理拥有均值行的表格每个单元格的样式,均值字段均加粗，其他需要比较的字段根据自身百分比显示颜色
 *              其中使用row-style无效，scope会导致无法覆盖样式
 *               同时由于我自定义了表格内容，哪里的样式会覆盖row的样式，所以只能单独对单元格设置
 * @param {*} info  每个单元格的信息
 * @return {*}
 */
const tableCellStyle = (info: any) => {
  if (info.row.date === '均值')
    return {
      'font-weight': 'bold'
    }
  else if (info.column.property != 'count' && info.column.property != 'date' && props.needAverage) {
    return {
      'background-color': `rgba(59, 157, 247,${getDecimalFromRange(info.row[info.column.property])})`
    }
  } else return {}
}

// 根据分页大小的切换来更新数据
// 这里将他赋值，用于根据传入的配置来选择是否开启该监听
/**
 * @description: 监听litmit，currentpage，gid的变化，改变后去重新请求数据
 * 如果是limit的变化，则需要把当前页置为1
 *
 *  对于Gid需要去监听props的，而不是本地的，因为是外部的改变
 * @return {*}
 */
const changePageLimit = watch(
  () => [
    paginationConfig2.limit,
    props.requestConfig?.otherOptions.gid,
    paginationConfig2.currentPage
  ],
  ([newLimit, newGid], [oldLimit, oldGid]) => {
    if (newLimit != oldLimit) {
      // 需要给分页按钮加上:current-page.sync="current_page" 配置，不然不生效
      // 当改变每页大小时把之前的缓存全部清除，重新开始
      paginationConfig2.currentPage = 1
      // resetTableData()
    }
    if (newGid != oldGid) reqconfig.otherOptions.gid = newGid

    if (newLimit != oldLimit || !tableData[paginationConfig2.currentPage] || newGid != oldGid) {
      getData()
    }
  },
  { deep: true }
)

// 监听传入的datalist的变化，然后去更新数据
const changeDataList = watch(
  () => [props.dataList],
  () => {
    getData()
  },
  {
    deep: true
  }
)

// 监听日期的变化，
const watchDateChange = watch(
  () => [props.requestConfig?.otherOptions.startTime, props.requestConfig?.otherOptions.endTime],
  () => {
    getData()
  },
  { deep: true }
)

/**
 * @description: 创建row-key优化表格性能
 * @return {*}
 */
const createRowKey = () => {
  return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
}

//如果没有日期就取消掉
if (!props.requestConfig?.otherOptions.startTime && !props.requestConfig?.otherOptions.endTime) {
  watchDateChange()
}

// 没传入datalist则取消该监听
if (!props.dataList) {
  changeDataList()
}

// 没有开启分页查询就关闭掉这个监听
if (!props.openPageQuery) {
  changePageLimit()
}

/**
 * @description: 拷贝一份配置文件
 * @return {*}
 */
const initpageConfig = () => {
  Object.assign(paginationConfig2, props.paginationConfig)
}

/**
 * @description: 初始化请求配置，用于把拷贝一份新的数据
 * @return {*}
 */
const initReqConfig = () => {
  Object.assign(reqconfig, props.requestConfig)
}

/**
 * @description: 表格排序
 * @param {*} data 获取到的数据
 * @return {*}
 */
const tableSortChange = (data: { column: any; prop: string; order: any }) => {
  let { order } = { ...data }
  if (order === 'ascending') order = 'asc'
  else if (order === 'descending') order = 'desc'
  else order = ''
  reqconfig.otherOptions.order = order
  getData()
}

/**
 * @description: 删除行
 * @param {*} url 请求地址
 * @param {*} row 行数据
 * @return {*}
 */
const deleteRow = (url: string, filedsInfo: any) => {
  axiosInstance
    .post(url, { ...filedsInfo })
    .then((data) => {
      analysisResCode(data).then(() => {
        getData()
      })
    })
    .catch((err) => {
      console.log(err)
    })
}

/**
 * @description: 下载表格数据
 * @return {*}
 */
const downLoadTable = () => {
  emits('downLoad', JSON.parse(JSON.stringify(tableData)))
}

/**
 * @description: 外部获取数据
 * @return {*}
 */
const outGetTableData = () => {
  return toRaw(tableData).flat()
}

// 定义暴露出去的方法
defineExpose({
  getData,
  resetTableData,
  deleteRow,
  downLoadTable,
  outGetTableData
})

onMounted(() => {
  initpageConfig()
  initReqConfig()
  if (props.loadingState !== undefined) {
    loading.value = props.loadingState
  }
  if (!props.openPageQuery) {
    getData()
  }
  // 去加载所有需要的资源
  initLoadResouce(resourceInfo).then((data) => {
    Object.assign(blobUrlInfo, data)
  })
})
</script>

<template>
  <div class="tableContent">
    <div class="filterBox" v-if="openFilterQuery">
      <!-- slot -->
      <div class="filterHeader">
        <span>查询条件</span>
      </div>
      <div class="filterBody">
        <el-form
          :inline="true"
          ref="queryFormRef"
          :model="queryFormData"
          class="queryForm"
          :label-position="'left'"
        >
          <!-- 所有的input查询框 -->
          <el-form-item :label="item.label" v-for="item in inputFieldsList" class="filterItem">
            <el-input
              v-model="queryFormData[item.name]"
              :placeholder="item.placeholder"
              clearable
            />
          </el-form-item>

          <!-- 所有选择框 -->
          <el-form-item :label="item.label" v-for="item in selectFieldsList" class="filterItem">
            <el-select v-model="queryFormData[item.name]" :placeholder="item.placeholder">
              <el-option v-for="val in item.otherOption" :label="val.cnName" :value="val.value" />
            </el-select>
          </el-form-item>

          <!-- 所有日期选择框 -->
          <el-form-item :label="item.label" v-for="item in dateFieldsList" class="filterItem">
            <el-date-picker
              v-model="queryFormData[item.name]"
              type="date"
              :placeholder="item.placeholder"
              clearable
            />
          </el-form-item>
        </el-form>
        <!-- 分割线 -->
        <!-- <el-divider class="queryPartition" content-position="center" direction="vertical" /> -->
        <div class="queryBox">
          <el-divider class="queryPartition" content-position="center" direction="vertical" />
          <div class="queryBtnBox">
            <el-button class="queryBtn" color="#165dff" @click="queryTableData">
              <el-icon><Search /></el-icon>查询
            </el-button>
            <el-button class="refreshBtn" color="#f2f3f5" @click="resetQueryForm(queryFormRef)">
              <el-icon><RefreshRight /></el-icon>重置
            </el-button>
          </div>
        </div>
      </div>
    </div>
    <!-- 分割线 -->
    <!-- <el-divider class="partition" content-position="center" /> -->
    <div class="tableTools">
      <div class="leftTools">
        <div class="leftToolsGroup" v-if="needLeftTools" style="display: flex">
          <el-button class="leftToolBtn" color="#165dff" @click="emits('addNewItem')">
            <el-icon><Plus /></el-icon>新增
          </el-button>
          <el-button
            class="leftToolBtn"
            color="#626aef"
            @click="emits('upload', outGetTableData())"
            v-if="needUpload"
          >
            <el-icon><Upload /></el-icon>上传
          </el-button>
        </div>
      </div>
      <div class="rightTools" v-if="needRightTools">
        <el-button
          v-if="needDownload"
          color="#f0f1f3"
          size="default"
          class="rightToolsItem"
          @click="downLoadTable"
        >
          <el-icon><Download /></el-icon>下载
        </el-button>

        <RegreshBtn @refresh-table="getData" :icon-size="toolsIconSize"></RegreshBtn>

        <FilterPopover
          :table-fields-info="tableFieldsInfo"
          :icon-size="toolsIconSize"
        ></FilterPopover>
      </div>
    </div>

    <div class="tableBox">
      <!-- 没有分页的时候需要重新计算一下data -->
      <el-table
        :data="openPageQuery ? tableData[paginationConfig2.currentPage] : tableDataNoPaging"
        style="width: 100%"
        class="tableBody"
        :cell-style="tableCellStyle"
        v-loading="loading"
        :row-key="createRowKey()"
        @sort-change="tableSortChange"
      >
        <el-table-column
          v-if="props.needRowindex"
          align="center"
          label="#"
          type="index"
          :index="computedRowIndex"
        />
        <template v-for="item in tableFieldsInfo">
          <el-table-column
            :prop="item.name"
            :label="item.cnName"
            :min-width="item.specialEffect?.type === FieldSpecialEffectType.DROPDOWN ? '170px' : ''"
            align="center"
            show-overflow-tooltip
            v-if="item.isShow"
            :sortable="item.needSort === true ? 'custorm' : false"
          >
            <template v-slot="scope">
              <!-- tag类 -->
              <el-tag
                v-if="item.specialEffect?.type === FieldSpecialEffectType.TAG"
                :type="scope.row[item.name] ? 'danger' : 'success'"
              >
                {{
                  scope.row[item.name]
                    ? item.specialEffect.othnerInfo.text[0]
                    : item.specialEffect.othnerInfo.text[1]
                }}
              </el-tag>

              <!-- 头像类 -->
              <el-image
                v-else-if="item.specialEffect?.type === FieldSpecialEffectType.IMG"
                :preview-teleported="true"
                :src="scope.row[item.name]"
                :preview-src-list="[scope.row[item.name]]"
                style="width: 35px; height: 35px"
                :fit="'fill'"
                :hide-on-click-modal="true"
              >
                <template #error>
                  <!--  -->
                  <img style="width: 35px; height: 35px" :src="blobUrlInfo.defaultHead" />
                </template>
              </el-image>

              <!-- 文字类 -->
              <el-text
                v-else-if="item.specialEffect?.type === FieldSpecialEffectType.TEXT"
                :type="
                  scope.row[item.name]
                    ? item.specialEffect.othnerInfo.color[0]
                    : item.specialEffect.othnerInfo.color[1]
                "
              >
                {{ scope.row[item.name] }}
              </el-text>

              <!-- 翻译类 -->
              <el-text v-else-if="item.specialEffect?.type === FieldSpecialEffectType.TRANSLATE">
                <el-icon
                  v-if="item.specialEffect.othnerInfo.icon"
                  style="padding-right: 8px"
                  :color="scope.row[item.name] ? '#409EFF' : '#F56C6C'"
                  ><icon-tabler-point-filled></icon-tabler-point-filled
                ></el-icon>
                {{
                  item.specialEffect.othnerInfo.translateText[scope.row[item.name]]
                    ? item.specialEffect.othnerInfo.translateText[scope.row[item.name]]
                    : '未知'
                }}
              </el-text>

              <!-- 状态类 -->
              <el-text v-else-if="item.specialEffect?.type === FieldSpecialEffectType.STATE">
                <span>
                  <el-icon
                    style="padding-right: 8px"
                    :color="scope.row[item.name] ? '#409EFF' : '#F56C6C'"
                    ><icon-tabler-point-filled></icon-tabler-point-filled
                  ></el-icon>
                  {{
                    scope.row[item.name]
                      ? item.specialEffect.othnerInfo.text[0]
                      : item.specialEffect.othnerInfo.text[1]
                  }}</span
                >
              </el-text>

              <!-- 开关类 -->

              <el-switch
                :active-value="1"
                :inactive-value="0"
                v-else-if="item.specialEffect?.type === FieldSpecialEffectType.SWITCH"
                v-model="scope.row[item.name]"
                :data="scope.row[item.name]"
                size="default"
              >
              </el-switch>

              <!-- 下拉菜单类 -->
              <el-dropdown
                trigger="click"
                v-else-if="item.specialEffect?.type === FieldSpecialEffectType.DROPDOWN"
              >
                <span
                  class="el-dropdown-link"
                  style="display: flex; align-items: center; cursor: pointer"
                >
                  <el-icon
                    style="padding-right: 8px"
                    :color="scope.row[item.name] ? '#409EFF' : '#F56C6C'"
                    ><icon-tabler-point-filled></icon-tabler-point-filled
                  ></el-icon>
                  {{
                    scope.row[item.name]
                      ? item.specialEffect.othnerInfo.text[0]
                      : item.specialEffect.othnerInfo.text[1]
                  }}

                  <el-icon class="el-icon--right"><arrow-down /></el-icon>
                </span>
                <template #dropdown>
                  <el-dropdown-menu>
                    <el-dropdown-item :command="{ value: true }">使用中</el-dropdown-item>
                    <el-dropdown-item :command="{ value: false }">已弃用</el-dropdown-item>
                  </el-dropdown-menu>
                </template>
              </el-dropdown>

              <el-text v-else>
                <!-- 其他列按默认方式显示 -->

                {{
                  props.needAverage &&
                  scope.row[item.name] !== undefined &&
                  item.name !== 'count' &&
                  item.name !== 'date'
                    ? scope.row[item.name] + '%'
                    : scope.row[item.name]
                }}
              </el-text>
            </template>
          </el-table-column>
        </template>

        <slot name="tableOperation"></slot>
      </el-table>
      <div class="userTablePaginationBox">
        <el-pagination
          class="userTablePagination"
          background
          :page-size="paginationConfig2.limit"
          :page-sizes="paginationConfig2.pagesizeList"
          table-layout="fixed"
          layout="prev, pager, next ,jumper ,sizes,total,"
          :total="paginationConfig2.total"
          :current-page.sync="paginationConfig2.currentPage"
          @current-change="handleCurrentChange"
          @size-change="handleSizeChange"
        />
      </div>
    </div>
  </div>
</template>

<style scoped>
.tableContent {
  margin: 0 auto;
  width: 100%;

  /* height: 100%; */
  box-shadow:
    0 4px 8px 0 rgba(0, 0, 0, 0.02),
    0 1px 3px 0 rgba(0, 0, 0, 0.02);
}
.filterBox,
.tableBox {
  width: 100%;
}

.filterBox {
  margin-top: 1%;
  min-height: 18%;
  display: flex;
  flex-direction: column;
}

.filterHeader,
.filterBody {
  width: 98%;
  margin: 0 auto;
}

.filterHeader {
  display: flex;
  align-items: center;
  color: black;
  font-size: 16px;
  font-weight: bold;
  /* background-color: lightblue; */
  /* margin-bottom: 1%; */
}

.filterBody {
  display: flex;
}

.queryBox {
  width: 10%;
  display: flex;
  justify-content: space-around;
}

.queryBtnBox {
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}

.refreshBtn {
  margin-top: 10%;
  margin-bottom: 10%;
}

.queryPartition {
  height: 90%;
}

.queryForm {
  width: 90%;
  display: flex;
  flex-wrap: wrap;
  /* justify-content: space-between; */
}

.filterItem {
  width: 20%;
  display: flex;
  align-items: center;
}

.partition {
  width: 98%;
  margin: 0 auto;
}

.tableTools {
  width: 98%;
  margin: 0 auto;
  display: flex;
  justify-content: space-between;
  margin-top: 1%;
}

.leftTools,
.rightTools {
  width: 10%;
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.rightTools {
  width: 5%;
}

.tableBox {
  width: 98%;
  /* height: 98%; */
  margin: 0 auto;
  /* margin-top: 0.5%;
  margin-bottom: 2%; */
}

.userTablePaginationBox {
  box-sizing: border-box;
  width: 98%;
  margin: 0% auto;
  padding: 1% 0;
  display: flex;
  justify-content: center;
}

.averageItem {
  font-size: 14px;
  color: #515b6f;
}

.normalItem {
  font-size: 14px;
  color: #515b6f;
  font-weight: 400;
}

.leftToolBtn {
  margin-right: 5px;
}
</style>
