<script setup lang="ts">
import { Delete, Plus, Edit } from '@element-plus/icons-vue'
import { ref, toRaw, watch } from 'vue'
import type { VxeFormProps, VxeGridProps, VxeModalProps } from 'vxe-table'
import { VxeModal, VxeForm, VxeButton } from 'vxe-pc-ui'
import { VxeGrid } from 'vxe-table'
import hud from '@/utils/hud'
import obj from '@/utils/obj'
import { BasicService } from '@/api/service/basic'
import {
  mj_form_button_group,
  mj_form_input,
  mj_action_update,
  mj_action_add,
  mj_action_remove,
} from './const'

import type { MjEditFormProps, MjGridProps, MjFormProps, MjGridEventProps } from './const'
import { percentWidth, px2px } from '@/utils'

// 属性
const props = withDefaults(defineProps<Props>(), {
  gridEvents: {} as any,
  multiple: true,
})

// 事件
const emit = defineEmits<{
  (e: 'edit-form-visible-changed', visible: boolean): void
  // 数据修改了（添加、更新、删除成功）
  (
    e: 'data-modified',
    val: {
      action: number
      data: any
    },
  ): void
  // 选中改变
  (e: 'select-change', items: any[], item: any): void
}>()

defineOptions({
  name: 'MjTable',
})

type Props = {
  grid: MjGridProps
  gridEvents?: MjGridEventProps
  searchParams?: any
  searchForm?: MjFormProps
  service?: BasicService<any>
  editForm?: MjEditFormProps
  hideEdit?: boolean
  hideRemove?: boolean
  hideAdd?: boolean
  hideSearch?: boolean
  selectable?: boolean
  multiple?: boolean
  hidePager?: boolean
  hideToolbar?: boolean
}

// 权限控制
const _hideEdit = ref(false)
const _hideRemove = ref(false)
const _hideAdd = ref(false)
const _hideCheckbox = ref(false)
const _hideSearch = ref(false)

// 权限控制
function initPermissions() {
  _hideEdit.value = props.hideEdit || false
  _hideRemove.value = props.hideRemove || false
  _hideAdd.value = props.hideAdd || false
  _hideSearch.value = props.hideSearch || false
  if (!props.editForm) {
    // 没有表单
    _hideEdit.value = true
    _hideAdd.value = true
  }
  if (props.selectable) {
    // 可选
    _hideRemove.value = true
  } else {
    _hideCheckbox.value = _hideRemove.value
  }
}

// 编辑表单弹框
const editModalVisible = ref(false)
const defaultEditModalOptions = {
  escClosable: true,
  showMaximize: true,
} as VxeModalProps
const editModalOptions = ref(defaultEditModalOptions)

// 不需要响应式
// 重置的表单数据
let resetFormData = {} as any
// 添加表单时的默认数据
let addFormData = {} as any

// 编辑表单
const defaultEditFormOptions = {
  titleWidth: percentWidth(0.04),
  titleBold: true,
  data: {},
} as VxeFormProps

const editFormOptions = ref(defaultEditFormOptions)

// 编辑表单事件
const editFormEvents = {
  submit() {
    // 解除响应式
    let submitData = toRaw(editFormOptions.value.data)

    if (submitData.id) {
      // 如果是编辑，只更新部分字段
      let filterEditFormData = resetFormData
      const keys = props.editForm?.updateFields as any[]
      if (keys.length) {
        submitData = obj.filterKeys(submitData, keys)
        filterEditFormData = obj.filterKeys(resetFormData, keys)
      }

      // 如果没有任何变化
      if (obj.isEqualIgnoreNumberString(submitData, filterEditFormData)) {
        hud.error('没有任何变化，无需提交')
        return
      }
    }
    // 请求前的处理
    const fn = props.editForm?.beforeSaveOrUpdate
    if (fn) {
      submitData = fn(submitData)
      if (!submitData) {
        return
      }
    }

    // 保存数据
    hud.load({
      task: async () => {
        await props.service?.saveOrUpdate(submitData)

        reload()
        editModalVisible.value = false
        emit('data-modified', {
          action: submitData.id ? mj_action_update : mj_action_add,
          data: submitData,
        })
      },
      option: '保存',
    })
  },
  reset() {
    editFormOptions.value.data = { ...resetFormData }
  },
}

// 初始化编辑表单
async function initEditForm() {
  // 弹框
  const editModalOptionsTmp = { ...defaultEditModalOptions }
  obj.cover(editModalOptionsTmp, props.editForm?.modal)
  editModalOptions.value = editModalOptionsTmp

  // 添加表单时的默认数据
  addFormData = {
    ...(props.editForm?.defaultFormData || {}),
  }

  // 编辑表单
  const editFormOptionsTmp = {
    ...defaultEditFormOptions,
  }
  obj.cover(editFormOptionsTmp, props.editForm?.form)
  editFormOptionsTmp.data = editFormOptions.value.data || {}
  editFormOptionsTmp.items = [
    ...(props.editForm?.form.items || []),
    {
      align: 'right',
      span: 24,
      itemRender: {
        name: mj_form_button_group,
        options: [
          {
            type: 'submit',
            content: '提交',
            status: 'primary',
            icon: 'vxe-icon-send',
          },
          { type: 'reset', content: '重置', icon: 'vxe-icon-repeat' },
        ],
      },
    },
  ]
  // 根据editFormOptionsTmp设置添加表单的默认值
  for (const item of editFormOptionsTmp.items) {
    if (!item.field) continue
    if (item.field in addFormData) continue
    const options = item.itemRender?.options
    if (!options?.length) continue
    addFormData[item.field] = options[0].value
  }
  editFormOptions.value = editFormOptionsTmp
}

// 表格
const gridRef = ref()

// 搜索表单
const defaultSearchFormOptions = {
  titleBold: true,
  data: {},
  collapseNode: true,
} as VxeFormProps

// 表格的配置
const checkedProp = 'checked'
const defaultGridOptions = {
  virtualXConfig: {
    enabled: false,
  },
  virtualYConfig: {
    enabled: false,
  },
  height: 'auto',
  showOverflow: true,
  border: false,
  proxyConfig: {
    ajax: {
      query: loadPageData,
    },
  },
  checkboxConfig: {
    highlight: true,
    checkField: checkedProp,
  },
  pagerConfig: {
    total: 0,
    currentPage: 1,
    pageSizes: [1, 2, 10, 25, 50],
    pageSize: 25,
    layouts: [
      'Home',
      'PrevJump',
      'PrevPage',
      'Number',
      'NextPage',
      'NextJump',
      'End',
      'Sizes',
      'FullJump',
      'Total',
    ],
  },
  sortConfig: {
    trigger: 'cell',
  },
  data: [],
} as VxeGridProps
const gridOptions = ref(defaultGridOptions)

// 初始化表格
async function initGrid() {
  // 表格的列
  const gridColumns = []
  if (!_hideCheckbox.value) {
    gridColumns.push({
      type: 'checkbox',
      width: px2px(100),
      slots: { header: 'checkbox_header', checkbox: 'checkbox_cell' },
    })
  }
  if (!_hideEdit.value) {
    gridColumns.push({
      title: '编辑',
      width: px2px(80),
      slots: { default: 'mj_edit_default' },
    })
  }

  // 表格合并
  const gridOptionsTmp = { ...defaultGridOptions, data: gridOptions.value.data }
  gridOptionsTmp.columns = gridColumns as any

  // 工具条
  if (!props.hideToolbar) {
    gridOptionsTmp.toolbarConfig = {
      slots: {
        buttons: 'toolbarButtons',
      },
    }
  }

  obj.cover(gridOptionsTmp, props.grid)

  for (const column of gridOptionsTmp.columns!) {
    if (column.width || column.minWidth) continue
    column.minWidth = 'auto'
  }

  // 搜索表单
  if (!_hideSearch.value) {
    const searchFormOptions = {
      ...defaultSearchFormOptions,
      data: gridOptions.value.formConfig?.data || {},
    }
    obj.cover(searchFormOptions, props.searchForm)
    searchFormOptions.items = [
      {
        field: 'keyword',
        title: '关键字',
        span: 4,
        itemRender: {
          name: mj_form_input,
          props: {
            clearable: true,
          },
        },
      },
      ...(props.searchForm?.items || []),
      {
        slots: { default: 'action' },
      },
    ]
    gridOptionsTmp.formConfig = searchFormOptions
  }

  gridOptions.value = gridOptionsTmp
}

// 监听配置
watch(
  [() => props.hideEdit, () => props.hideAdd, () => props.hideRemove, () => props.selectable],
  async () => {
    initPermissions()
  },
  {
    immediate: true,
    // 深度监听
    deep: true,
  },
)

watch(
  [() => props.editForm],
  async () => {
    initEditForm()
  },
  {
    immediate: true,
    // 深度监听
    deep: true,
  },
)

watch(
  [() => props.grid, () => props.searchForm, () => _hideCheckbox.value, () => _hideEdit.value],
  async () => {
    initGrid()
  },
  {
    immediate: true,
    // 深度监听
    deep: true,
  },
)

// 加载数据
async function loadPageData(params: any) {
  const { page } = params
  const form = toRaw(gridOptions.value.formConfig?.data) || {}
  // 搜索条件
  let findPageParams = {
    ...(props.searchParams || {}),
    ...form,
  }

  let res
  if (props.grid.loadData) {
    res = await props.grid.loadData(page.currentPage, page.pageSize, findPageParams)
  } else {
    try {
      if (props.grid.beforeLoadData) {
        findPageParams = props.grid.beforeLoadData(findPageParams)
        // 停止请求
        if (!findPageParams) return
      }
      res = await props.service?.findPage(page.currentPage, page.pageSize, findPageParams)
    } finally {
      if (props.grid.afterLoadData) {
        res = props.grid.afterLoadData(res)
      }
    }
  }

  return {
    result: res?.rows,
    page: {
      total: res?.totalRows,
    },
  }
}

// 是否可以删除
const removeDisable = ref(true)
const removeText = ref('删除')

// 新增
function add() {
  // 这里直接使用引用数据
  resetFormData = { ...addFormData }

  editModalOptions.value.title = '新增'
  // 复制对象，避免修改原数据
  editFormOptions.value.data = { ...resetFormData }
  editModalVisible.value = true
}

// 编辑
function edit(row: any) {
  resetFormData = { ...row }

  editModalOptions.value.title = '编辑'
  // 复制对象，避免修改原数据
  editFormOptions.value.data = { ...resetFormData }
  editModalVisible.value = true
}

// 删除
function remove() {
  const records = gridRef.value.getCheckboxRecords()
  hud.confirmLoad({
    dialog: `是否要删除【${records.length}】条数据？`,
    task: async () => {
      await props.service?.remove(records.map((r: any) => r.id))

      gridRef.value.clearCheckboxRow()
      tableCheckChange()
      reload()
      emit('data-modified', {
        action: mj_action_remove,
        data: toRaw(records),
      })
    },
    option: '删除',
  })
}

// checkbox处理
function toggleAllCheckboxEvent() {
  gridRef.value.toggleAllCheckboxRow()
  tableCheckChange()
  emit('select-change', gridRef.value.getCheckboxRecords(), undefined)
}

function toggleCheckboxEvent(row: any) {
  gridRef.value.toggleCheckboxRow(row)
  tableCheckChange()
  emit('select-change', gridRef.value.getCheckboxRecords(), row)
}

function tableCheckChange() {
  const len = gridRef.value.getCheckboxRecords().length
  if (len) {
    removeText.value = `删除(${len})`
    removeDisable.value = false
  } else {
    removeText.value = '删除'
    removeDisable.value = true
  }
}

function reload() {
  gridRef.value.commitProxy('reload')
}

// 监听表单对话框的显示和隐藏
watch(
  () => editModalVisible.value,
  visible => {
    emit('edit-form-visible-changed', visible)
  },
)

// 搜索表单重置
function searchFormReset() {
  gridOptions.value.formConfig!.data = {}
  gridRef.value.commitProxy('reload')

  const fn = props.grid.afterReset
  fn && fn()
}

defineExpose({
  getData() {
    return {
      editForm: editFormOptions.value,
      grid: gridOptions.value,
      resetFormData,
    }
  },
  updateSearchFormData(data: any) {
    if (!data) return
    const rawData = toRaw(gridOptions.value.formConfig!.data)
    gridOptions.value.formConfig!.data = obj.cover(rawData, data)
  },
  reload,
  getGrid() {
    return gridRef.value
  },
  emitUpdate(data: any) {
    emit('data-modified', {
      action: mj_action_update,
      data,
    })
  },
})
</script>

<template>
  <div class="mj-table">
    <vxe-grid ref="gridRef" v-bind="gridOptions" v-on="gridEvents">
      <!-- 插槽传递给子组件 -->
      <template v-for="(_, slotName) in $slots" #[slotName]="slotProps">
        <slot v-if="slotName !== 'toolbarButtonsExtra'" :name="slotName" v-bind="slotProps || {}" />
      </template>

      <!-- 每一行的编辑按钮 -->
      <template #mj_edit_default="{ row }">
        <el-button :icon="Edit" type="primary" @click.stop="edit(row)" />
      </template>

      <!-- 表格顶部的按钮 -->
      <template #toolbarButtons>
        <el-button v-if="!_hideAdd" type="primary" :icon="Plus" @click="add">新增</el-button>
        <el-button
          v-if="!_hideRemove"
          type="danger"
          :disabled="removeDisable"
          :icon="Delete"
          @click="remove"
          >{{ removeText }}</el-button
        >
        <slot name="toolbarButtonsExtra" />
      </template>

      <!-- 表头的checkbox -->
      <template #checkbox_header="{ checked, indeterminate }">
        <span class="custom-checkbox" @click.stop="toggleAllCheckboxEvent">
          <i v-if="indeterminate" class="vxe-icon-square-minus-fill" />
          <i v-else-if="checked" class="vxe-icon-square-checked-fill" />
          <i v-else class="vxe-icon-checkbox-unchecked" />
        </span>
      </template>

      <!-- 每一行的checkbox -->
      <template #checkbox_cell="{ row, checked, indeterminate }">
        <span class="custom-checkbox" @click.stop="toggleCheckboxEvent(row)">
          <i v-if="indeterminate" class="vxe-icon-square-minus-fill" />
          <i v-else-if="checked" class="vxe-icon-square-checked-fill" />
          <i v-else class="vxe-icon-checkbox-unchecked" />
        </span>
      </template>

      <template #action>
        <vxe-button icon="vxe-icon-repeat" @click="searchFormReset">重置</vxe-button>
        <vxe-button type="submit" icon="vxe-icon-search" status="primary">搜索</vxe-button>
      </template>
    </vxe-grid>

    <vxe-modal v-model="editModalVisible" v-bind="editModalOptions">
      <template #default>
        <vxe-form v-bind="editFormOptions" v-on="editFormEvents" />
      </template>
    </vxe-modal>
  </div>
</template>

<style lang="scss" scoped>
@use './index.scss' as *;
</style>
