<!--
 * @Author: zhaojinfeng 121016171@qq.com
 * @Date: 2023-07-18 12:30:07
 * @LastEditors: zhaojinfeng 121016171@qq.com
 * @LastEditTime: 2023-07-23 13:36:40
 * @FilePath: \vue3\packages\select-table-modal\index.vue
 * @Description: 模态框选择表格
 *
-->
<template>
  <span v-if="!hideButton" @click="open">
    <slot>
      <el-button type="primary" plain>点击选择</el-button>
    </slot>
  </span>
  <el-config-provider :locale="locale">
    <el-dialog v-model="showDialog" :title="title" :width="width" append-to-body :z-index="zIndex" @open="getList" @closed="closed">
      <el-form ref="queryRef" :model="queryParams" inline>
        <el-form-item v-for="formItem in formItems" :key="formItem.prop" :label="formItem.label" :prop="formItem.prop">
          <el-select v-if="formItem.selectOptions" :placeholder="formItem.placeholder">
            <el-option v-for="option in formItem.selectOptions" :key="option.value" :value="option.value">
              {{ option.label }}
            </el-option>
          </el-select>
          <el-input v-else v-model="queryParams[formItem.prop]" :placeholder="formItem.placeholder" />
        </el-form-item>
        <el-form-item>
          <el-button type="primary" :loading="loading" @click="getList">
            查询
          </el-button>
          <el-button :loading="loading" @click="resetQuery">
            重置
          </el-button>
        </el-form-item>
      </el-form>
      <el-table
        v-loading="loading"
        border
        :data="tableData"
        highlight-current-row
        :row-key="rowKey"
        :empty-text="emptyText"
        :reserve-selection="multiple"
        @current-change="handleCurrentChange"
        @selection-change="handleSelectionChange"
      >
        <el-table-column v-if="multiple" type="selection" width="55" align="center" />
        <el-table-column
          v-for="column in columns"
          :key="column.prop"
          :align="column.align || 'center'"
          :label="column.label"
          :prop="column.prop"
          :formatter="column.formatter"
        >
          <template v-if="isExist(column.prop)" #default="{ row, $index }">
            <!-- element自带的属性$index -->
            <!-- eslint-disable-next-line vue/valid-attribute-name -->
            <slot :name="`column-${column.prop}`" :row="row" :$index="$index" />
          </template>
        </el-table-column>
      </el-table>
      <div w-full h-70px relative>
        <el-pagination
          v-model:current-page="queryParams.pageNum"
          v-model:page-size="queryParams.pageSize"
          absolute
          right-50px
          top-20px
          background
          hide-on-single-page
          :layout="layout"
          :page-sizes="pageSizes"
          :pager-count="pagerCount"
          :total="total"
          @size-change="handleSizeChange"
          @current-change="getList"
        />
      </div>
      <template #footer>
        <el-space>
          <el-button :disabled="disabled" type="primary" @click="confirm">
            确定
          </el-button>
          <el-button @click="showDialog = false">
            取消
          </el-button>
        </el-space>
      </template>
    </el-dialog>
  </el-config-provider>
</template>

<script lang="ts" setup name="ThSelectTableModal">
import type { FormInstance, TableColumnCtx, TableInstance } from 'element-plus'
import locale from 'element-plus/lib/locale/lang/zh-cn'

const props = withDefaults(
  defineProps<{
    emptyText?: string
    value?: any
    rowKey?: string
    show?: boolean
    columns: Partial<TableColumnCtx<any>>[]
    formItems: FormItem[]
    defaultParams?: Record<string, any>
    request: (args: any) => Promise<Page>
    layout?: string
    pageSizes?: number[]
    pagerCount?: number
    hideButton?: boolean
    hidePagination?: boolean
    title?: string
    width?: string | number
    /** 是否多选 */
    multiple?: boolean
    zIndex?: number
    multipleFormattter?: (row: Record<string, any>) => any
  }>(),
  {
    hidePagination: false,
    layout: 'total, sizes, prev, pager, next, jumper',
    rowKey: 'id',
    pageSizes: () => [10, 20, 30, 50],
    pagerCount: () => document.body.clientWidth < 992 ? 5 : 7,
    hideButton: false,
    width: '1000px',
    title: '选择',
    emptyText: '暂无数据',
    zIndex: 2023,
    multipleFormattter(row: Record<string, any>) {
      return { ...row }
    },
  },
)
const emit = defineEmits<{
  (event: 'update:show', show: boolean): void
  (event: 'update:value', show: any): void
  (event: 'confirm', row: any): void
}>()
interface FormItem {
  label: string
  prop: string
  placeholder?: string
  selectOptions?: {
    label: string
    value: string
  }[]
}
const showDialog = ref(false)

const slots = Object.keys(useSlots())

function isExist(prop: string) {
  return slots.includes(`column-${prop}`)
}

let tableData = $ref<any[]>([])
let selection = $ref<any>()
let selections = $ref<Record<string, any>[] | null>(null)

const disabled = computed(() => {
  if (props.multiple)
    return !selections?.length

  return !selection
})

function open() {
  if (props.hideButton)
    emit('update:show', true)
  else
    showDialog.value = true
}

let total = $ref(0)

function handleCurrentChange(val?: any) {
  selection = val
}

/* 表单绑定的ref */
const queryRef = $ref<FormInstance>(null)
const tableRef = $ref<TableInstance>(null)

/* 初始加载 */
let loading = $ref(false)

/* 查询参数 */
const queryParams = reactive({
  pageNum: 1,
  pageSize: 10,
})

function initSelections() {
  const rows = tableData.map(props.multipleFormattter)
  tableRef?.toggleRowSelection(rows, true)
}
function initSelection() {
  const row = tableData.find(row => row[props.rowKey] === props.value)
  if (row)
    tableRef?.setCurrentRow(row)
}

/** 查询表格数据 */
async function getList() {
  loading = true
  tableData = []
  try {
    const response = await props.request({
      ...props.defaultParams,
      ...queryParams,
    })
    tableData = response.rows
    total = response.total
    if (props.multiple)
      initSelections()

    else
      initSelection()
  }
  finally {
    loading = false
  }
}

/** 多选框选中数据 */
function handleSelectionChange(rows: any[]) {
  selections = rows.map(props.multipleFormattter)
}
function confirm() {
  if (props.multiple) {
    emit('confirm', selections)
    emit('update:value', selections)
  }
  else {
    emit('confirm', selection)
    emit('update:value', selection && selection[props.rowKey])
  }

  if (props.hideButton)
    emit('update:show', false)
  else
    showDialog.value = false
}

/** 重置按钮操作 */
function resetQuery() {
  queryRef?.resetFields()
  queryParams.pageNum = 1
  getList()
}
function handleSizeChange(val: number) {
  if (queryParams.pageNum * val > total)
    queryParams.pageNum = 1
}

function closed() {
  queryRef?.resetFields()
  tableData = []
  queryParams.pageNum = 1
  selection = undefined
}

watch(() => props.show,
  (show) => {
    showDialog.value = show
  })
</script>
