<template>
  <div class="common-table">
    <div class="addBox">
      <el-button v-if="showAdd" type="primary" @click="addTableData">
        {{ addBtnTxt }}
      </el-button>
    </div>
    <el-table v-loading="loading" :data="tableData" border style="width: 100%" v-bind="$attrs">
      <!-- 动态列 -->
      <el-table-column
        v-for="col in visibleColumns"
        :key="col.prop"
        :prop="col.prop"
        :width="col.width"
        :min-width="col.minWidth"
        :fixed="col.fixed"
        :align="col.align || 'left'"
        :show-overflow-tooltip="col.showOverflowTooltip"
      >
        <!-- 自定义表头：显示必填红色星号 -->
        <template #header>
          <span v-if="checkColumnRequired(col)" class="required-star"> *</span>
          <span>{{ col.label }}</span>
        </template>

        <template #default="{ row, $index }">
          <!-- 可编辑列：始终显示编辑组件 -->
          <div v-if="col.editable !== false">
            <el-form-item
              :rules="getColumnRules(col)"
              :error="getRowError(row, col.prop)"
              style="margin-bottom: 0"
            >
              <!-- 输入框（文本/数字/textarea） -->
              <el-input
                v-if="col.editType === 'input'"
                :model-value="getInputDisplayValue(row, col)"
                :placeholder="col.placeholder || `请输入${col.label}`"
                :clearable="true"
                :type="col.inputType === 'textarea' ? 'textarea' : 'text'"
                :rows="col.rows || 2"
                :disabled="col.disabled"
                @update:model-value="val => handleInputChange(row, col, val, $index)"
                @blur="handleInputBlur(row, col, $index)"
              />
              <!-- 下拉选择 -->
              <el-select
                v-else-if="col.editType === 'select'"
                :model-value="row[col.prop]"
                :placeholder="col.placeholder || `请选择${col.label}`"
                :clearable="true"
                filterable
                :allow-create="col.allowCreate"
                :default-first-option="col.allowCreate"
                :remote="!!col.api"
                :remote-method="keyword => handleRemoteSearch(keyword, col)"
                :loading="remoteLoading[col.prop]"
                :disabled="col.disabled"
                @update:model-value="val => handleSelectChange(row, col, val, $index)"
                @focus="() => loadRemoteOptions(col)"
              >
                <el-option
                  v-for="opt in getSelectOptions(col)"
                  :key="opt.value"
                  :label="opt.label"
                  :value="opt.value"
                />
              </el-select>

              <!-- 日期选择器 -->
              <el-date-picker
                v-else-if="col.editType === 'date'"
                :model-value="row[col.prop]"
                type="date"
                :placeholder="`选择${col.label}`"
                :value-format="col.valueFormat || 'YYYY-MM-DD'"
                style="width: 100%"
                :disabled="col.disabled"
                :disabled-date="getDisabledDateFn(col)"
                @update:model-value="val => handleCellChange(row, col, val, $index)"
              />

              <!-- 月份选择器 -->
              <el-date-picker
                v-else-if="col.editType === 'month'"
                :model-value="row[col.prop]"
                type="month"
                :placeholder="`选择${col.label}`"
                :value-format="col.valueFormat || 'YYYY-MM'"
                style="width: 100%"
                :disabled="col.disabled"
                :disabled-date="getDisabledDateFn(col)"
                @update:model-value="val => handleCellChange(row, col, val, $index)"
              />

              <!-- 日期范围 -->
              <el-date-picker
                v-else-if="col.editType === 'daterange'"
                :model-value="row[col.prop]"
                type="daterange"
                range-separator="至"
                :start-placeholder="col.startPlaceholder || '开始日期'"
                :end-placeholder="col.endPlaceholder || '结束日期'"
                :value-format="col.valueFormat || 'YYYY-MM-DD'"
                style="width: 100%"
                :disabled="col.disabled"
                :disabled-date="getDisabledDateFn(col)"
                @update:model-value="val => handleCellChange(row, col, val, $index)"
              />

              <!-- 月份范围 -->
              <el-date-picker
                v-else-if="col.editType === 'monthrange'"
                :model-value="row[col.prop]"
                type="monthrange"
                range-separator="至"
                :start-placeholder="col.startPlaceholder || '开始月份'"
                :end-placeholder="col.endPlaceholder || '结束月份'"
                :value-format="col.valueFormat || 'YYYY-MM'"
                style="width: 100%"
                :disabled="col.disabled"
                :disabled-date="getDisabledDateFn(col)"
                @update:model-value="val => handleCellChange(row, col, val, $index)"
              />

              <!-- 上传组件 -->
              <div v-else-if="col.editType === 'upload'" class="table-upload-wrapper">
                <el-upload
                  :file-list="getFileListDisplay(row[col.prop])"
                  :action="col.action || '/api/upload'"
                  :headers="col.headers"
                  :multiple="col.multiple || false"
                  :limit="col.limit || 1"
                  :accept="col.accept"
                  :disabled="col.disabled"
                  :auto-upload="true"
                  :show-file-list="false"
                  :before-upload="file => beforeUpload(file, col)"
                  :on-success="
                    (res, file, fileList) => handleUploadSuccess(row, col, res, file, fileList)
                  "
                  :on-error="
                    (err, file, fileList) => handleUploadError(row, col, err, file, fileList)
                  "
                >
                  <el-button size="small" type="primary">点击上传</el-button>
                </el-upload>
                <div class="upload-file-list" v-if="getFileListDisplay(row[col.prop]).length">
                  <div
                    v-for="(file, idx) in getFileListDisplay(row[col.prop])"
                    :key="idx"
                    class="file-item"
                  >
                    <span class="file-name">{{ file.name || file.url }}</span>
                    <el-button link type="danger" size="small" @click="removeFile(row, col, idx)"
                      >删除</el-button
                    >
                  </div>
                </div>
              </div>

              <!-- 自定义渲染（插槽） -->
              <slot
                v-else
                :name="`edit-${col.prop}`"
                :row="row"
                :column="col"
                :updateValue="val => handleCellChange(row, col, val, $index)"
              />
            </el-form-item>
          </div>

          <!-- 不可编辑列：只读显示 -->
          <div v-else>
            <slot :name="`column-${col.prop}`" :row="row" :column="col" :value="row[col.prop]">
              <span v-if="col.editType === 'select'">
                {{ getSelectLabel(col, row[col.prop]) }}
              </span>
              <span v-else-if="col.editType === 'upload' && row[col.prop]">
                <span v-for="(file, idx) in getFileListDisplay(row[col.prop])" :key="idx">
                  <el-link type="primary" @click="previewFile(file)">{{
                    file.name || file.url
                  }}</el-link>
                  <span v-if="idx < getFileListDisplay(row[col.prop]).length - 1">, </span>
                </span>
              </span>
              <span v-else-if="col.formatter">
                {{ col.formatter(row[col.prop], row) }}
              </span>
              <span v-else>{{ row[col.prop] || '-' }}</span>
            </slot>
          </div>
        </template>
      </el-table-column>

      <!-- 操作栏 -->
      <el-table-column
        v-if="showOperation"
        :label="operationLabel"
        :width="operationWidth"
        :fixed="operationFixed"
        align="center"
      >
        <template #default="{ row, $index }">
          <div class="operation-buttons">
            <slot name="operation" :row="row" :index="$index">
              <el-button link type="danger" size="small" @click="handleDelete(row)">删除</el-button>
            </slot>
          </div>
        </template>
      </el-table-column>

      <!-- 额外插槽列 -->
      <slot name="append" />
    </el-table>

    <!-- 分页组件 -->
    <div v-if="showPagination" class="table-pagination">
      <el-pagination
        v-model:current-page="currentPage"
        v-model:page-size="pageSize"
        :page-sizes="pageSizes"
        :background="paginationBackground"
        layout="total, sizes, prev, pager, next, jumper"
        :total="total"
        @size-change="handleSizeChange"
        @current-change="handleCurrentChange"
      />
    </div>

    <!-- 文件预览弹窗 -->
    <el-dialog v-model="previewDialogVisible" :title="previewFileName" width="60%" destroy-on-close>
      <div class="preview-container">
        <img v-if="previewFileType === 'image'" :src="previewUrl" style="max-width: 100%" />
        <iframe
          v-else-if="previewFileType === 'pdf'"
          :src="previewUrl"
          style="width: 100%; height: 70vh"
        ></iframe>
        <div v-else class="preview-unsupported">暂不支持预览此类型文件</div>
      </div>
    </el-dialog>
  </div>
</template>

<script setup>
import { ref, computed, watch, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import { getDicts } from '@/api/system/dict/data'
import useDictStore from '@/store/modules/dict'
import request from '@/utils/request'
import dayjs from 'dayjs'

// ==================== Props ====================
const props = defineProps({
  tableData: { type: Array, default: () => [] },
  columns: { type: Array, default: () => [] },
  rowIdKey: { type: String, default: 'id' },
  showOperation: { type: Boolean, default: true },
  operationLabel: { type: String, default: '操作' },
  operationWidth: { type: String, default: '120' },
  operationFixed: { type: String, default: 'right' },
  showPagination: { type: Boolean, default: false },
  total: { type: Number, default: 0 },
  page: { type: Number, default: 1 },
  limit: { type: Number, default: 20 },
  pageSizes: { type: Array, default: () => [10, 20, 30, 50] },
  paginationBackground: { type: Boolean, default: true },
  loading: { type: Boolean, default: false },
  showAdd: { type: Boolean, default: true },
  addBtnTxt: { type: String, default: '新增' }
})

const emit = defineEmits([
  'update:page',
  'update:limit',
  'update:tableData',
  'delete',
  'selectChange',
  'uploadSuccess',
  'add',
  'cell-change'
])

// ==================== 内部状态 ====================
// 行级错误信息 { [rowId]: { [prop]: errorMsg } }
const rowErrors = ref({})
// 远程选项存储
const remoteOptions = ref({})
const remoteLoading = ref({})
// 字典加载标记
const dictLoaded = ref(new Set())
// 当前页码/每页条数
const currentPage = ref(props.page)
const pageSize = ref(props.limit)

// 预览弹窗状态
const previewDialogVisible = ref(false)
const previewUrl = ref('')
const previewFileName = ref('')
const previewFileType = ref('')

// ==================== 计算属性 ====================
const visibleColumns = computed(() => {
  return props.columns.filter(col => {
    if (typeof col.visible === 'function') return col.visible()
    return col.visible !== false
  })
})

// 检查列是否必填
function checkColumnRequired(col) {
  if (col.required === true) return true
  if (col.rules && Array.isArray(col.rules)) {
    return col.rules.some(rule => rule.required === true)
  }
  return false
}

// 获取行的错误信息
function getRowError(row, prop) {
  const rowId = row[props.rowIdKey]
  return rowErrors.value[rowId]?.[prop] || ''
}

// 获取列校验规则
function getColumnRules(col) {
  if (col.rules) return col.rules
  if (col.required) {
    return [{ required: true, message: `${col.label}不能为空`, trigger: 'blur' }]
  }
  return []
}

// 获取 Select 选项列表
function getSelectOptions(col) {
  const key = col.prop
  if (col.dictType) {
    const dictStore = useDictStore()
    const dictOpts = dictStore.getDict(col.dictType) || []
    return dictOpts.map(opt => ({ value: opt.value, label: opt.label, raw: opt }))
  }
  if (col.api) {
    return (remoteOptions.value[key] || []).map(opt => ({
      value: opt.value,
      label: opt.label,
      raw: opt.raw
    }))
  }
  if (col.options) {
    return col.options.map(opt => ({ value: opt.value, label: opt.label, raw: opt.raw }))
  }
  return []
}

// 获取 Select 选中项的 label
function getSelectLabel(col, value) {
  const options = getSelectOptions(col)
  if (value === undefined || value === null) return '-'
  if (col.multiple && Array.isArray(value)) {
    return value.map(v => options.find(opt => opt.value === v)?.label || v).join(', ')
  }
  const found = options.find(opt => opt.value === value)
  return found ? found.label : value
}

// ==================== 数字输入格式化与解析 ====================
// 修改 getInputDisplayValue：所有类型优先使用临时编辑值
function getInputDisplayValue(row, col) {
  const rowId = row[props.rowIdKey]
  const editingKey = `${rowId}_${col.prop}`
  // 1. 正在编辑中 → 显示临时输入值
  if (editingTempValue.value[editingKey] !== undefined) {
    return editingTempValue.value[editingKey]
  }

  const { inputType } = col
  let rawValue = row[col.prop]

  // 2. 非数字类型直接返回原始值
  if (!['integer', 'decimalNumber', 'decimal'].includes(inputType)) {
    return rawValue ?? ''
  }

  // 3. 数字类型：失焦状态下格式化显示（千分位 + 小数位）
  if (rawValue === undefined || rawValue === null) return ''
  let num = typeof rawValue === 'number' ? rawValue : parseFloat(rawValue)
  if (isNaN(num)) return ''

  const { decimalDigits = 2 } = col
  num = parseFloat(num.toFixed(decimalDigits))
  const parts = num.toString().split('.')
  parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',')
  return parts.join('.')
}

// 临时编辑值存储（用于聚焦时保留用户输入原文）
const editingTempValue = ref({})

// 输入时实时处理（过滤非法字符，并保留临时编辑值）
function handleInputChange(row, col, rawValue, rowIndex) {
  const { inputType, decimalDigits = 2 } = col
  let filtered = rawValue

  if (inputType === 'integer') {
    filtered = rawValue.replace(/[^-\d]/g, '')
    if (filtered === '-') {
      // 保留负号
    } else if (filtered !== '') {
      let num = parseInt(filtered, 10)
      if (isNaN(num)) filtered = ''
      else filtered = num.toString()
    }
  } else if (inputType === 'decimalNumber') {
    let temp = rawValue.replace(/[^-\d.]/g, '')
    if (temp.startsWith('-')) {
      temp = '-' + temp.slice(1).replace(/-/g, '')
    } else {
      temp = temp.replace(/-/g, '')
    }
    if (temp === '-') {
      filtered = '-'
    } else {
      const pointIndex = temp.indexOf('.')
      if (pointIndex !== -1) {
        const intPart = temp.slice(0, pointIndex).replace(/[^\d-]/g, '')
        let decPart = temp.slice(pointIndex + 1).replace(/\D/g, '')
        if (decPart.length > decimalDigits) decPart = decPart.slice(0, decimalDigits)
        filtered = intPart + (decPart ? '.' + decPart : '.')
      } else {
        filtered = temp
      }
    }
  } else if (inputType === 'decimal') {
    let temp = rawValue.replace(/[^\d.]/g, '')
    const pointIndex = temp.indexOf('.')
    if (pointIndex !== -1) {
      const intPart = temp.slice(0, pointIndex).replace(/\D/g, '')
      let decPart = temp.slice(pointIndex + 1).replace(/\D/g, '')
      if (decPart.length > decimalDigits) decPart = decPart.slice(0, decimalDigits)
      filtered = intPart + (decPart ? '.' + decPart : '.')
    } else {
      filtered = temp
    }
  } else {
    // textarea 或普通文本
    filtered = rawValue
  }

  // const rowId = row[props.rowIdKey]
  // const editingKey = `${rowId}_${col.prop}`
  // // 保证响应式更新
  // editingTempValue.value = {
  //   ...editingTempValue.value,
  //   [editingKey]: filtered
  // }
  const rowId = row[props.rowIdKey]
  const editingKey = `${rowId}_${col.prop}`
  // 直接赋值，无需展开整个对象
  editingTempValue.value[editingKey] = filtered
}

// 失焦时：将临时编辑值转换为真实值并更新到表格数据
function handleInputBlur(row, col, rowIndex) {
  const rowId = row[props.rowIdKey]
  const editingKey = `${rowId}_${col.prop}`
  const rawStr = editingTempValue.value[editingKey]

  // 没有临时编辑值，说明用户未输入任何字符，直接返回
  if (rawStr === undefined) {
    return
  }

  delete editingTempValue.value[editingKey] // 清除临时值

  // 用户清空了输入框
  if (rawStr === '') {
    const newTableData = [...props.tableData]
    newTableData[rowIndex] = { ...row, [col.prop]: null }
    emit('update:tableData', newTableData)
    validateField(newTableData[rowIndex], col.prop)
    return
  }

  const { inputType, decimalDigits = 2 } = col
  let finalValue = null

  if (inputType === 'integer') {
    const match = rawStr.match(/^-?\d+/)
    if (match) {
      const num = parseInt(match[0], 10)
      if (!isNaN(num)) finalValue = num
    }
  } else if (inputType === 'decimalNumber') {
    const num = parseFloat(rawStr)
    if (!isNaN(num)) finalValue = parseFloat(num.toFixed(decimalDigits))
  } else if (inputType === 'decimal') {
    const num = parseFloat(rawStr)
    if (!isNaN(num) && num >= 0) finalValue = parseFloat(num.toFixed(decimalDigits))
  } else {
    // textarea 或普通文本
    finalValue = rawStr
  }

  const newTableData = [...props.tableData]
  newTableData[rowIndex] = { ...row, [col.prop]: finalValue }
  emit('update:tableData', newTableData)
  emit('cell-change', {
    row: newTableData[rowIndex],
    col,
    newValue: finalValue,
    oldValue: row[col.prop],
    rowIndex,
    prop: col.prop
  })
  validateField(newTableData[rowIndex], col.prop)
}

// ==================== 数据更新与校验 ====================
// 处理单元格值变更
function handleCellChange(row, col, newValue, rowIndex) {
  const rowId = row[props.rowIdKey]
  const oldValue = row[col.prop]
  if (oldValue === newValue) return

  // 更新数据
  const newTableData = [...props.tableData]
  newTableData[rowIndex] = { ...row, [col.prop]: newValue }
  emit('update:tableData', newTableData)
  emit('cell-change', {
    row: newTableData[rowIndex],
    col,
    newValue,
    oldValue,
    rowIndex,
    prop: col.prop
  })
  // 触发字段校验
  validateField(newTableData[rowIndex], col.prop)
}

// 处理 Select 变化（支持额外字段赋值）
function handleSelectChange(row, col, value, rowIndex) {
  const newTableData = [...props.tableData]
  const updatedRow = { ...row, [col.prop]: value }

  // 处理额外字段赋值
  if (col.onChangeExtraFields) {
    const options = getSelectOptions(col)
    const opt = options.find(o => o.value === value)
    if (opt?.raw) {
      for (const [targetProp, sourceKey] of Object.entries(col.onChangeExtraFields)) {
        updatedRow[targetProp] = opt.raw[sourceKey]
      }
    }
  }

  newTableData[rowIndex] = updatedRow
  emit('update:tableData', newTableData)
  emit('selectChange', col.prop, value, col)
  validateField(updatedRow, col.prop)
}

// 校验单个字段
async function validateField(row, prop) {
  const col = props.columns.find(c => c.prop === prop)
  if (!col) return true

  const rules = getColumnRules(col)
  if (!rules.length) return true

  const value = row[prop]
  const rowId = row[props.rowIdKey]
  let errorMsg = ''

  for (const rule of rules) {
    if (rule.required && (value === undefined || value === null || value === '')) {
      errorMsg = rule.message || `${col.label}不能为空`
      break
    }
    if (rule.pattern && !rule.pattern.test(String(value))) {
      errorMsg = rule.message || `${col.label}格式不正确`
      break
    }
    if (rule.validator && typeof rule.validator === 'function') {
      try {
        await rule.validator(null, value, () => {})
      } catch (err) {
        errorMsg = err.message
        break
      }
    }
  }

  if (!rowErrors.value[rowId]) {
    rowErrors.value[rowId] = {}
  }
  rowErrors.value[rowId][prop] = errorMsg
  return !errorMsg
}

// ==================== 全局校验方法（暴露给父组件） ====================
function validateAll() {
  const errors = []
  const allValid = []

  for (let i = 0; i < props.tableData.length; i++) {
    const row = props.tableData[i]
    const rowId = row[props.rowIdKey]
    let rowValid = true

    for (const col of visibleColumns.value) {
      if (col.editable === false) continue
      const rules = getColumnRules(col)
      if (rules.length === 0) continue

      const value = row[col.prop]
      let errorMsg = ''

      for (const rule of rules) {
        if (rule.required && (value === undefined || value === null || value === '')) {
          errorMsg = rule.message || `${col.label}不能为空`
          break
        }
        if (rule.pattern && !rule.pattern.test(String(value))) {
          errorMsg = rule.message || `${col.label}格式不正确`
          break
        }
      }

      if (errorMsg) {
        rowValid = false
        errors.push({
          rowIndex: i,
          rowId,
          prop: col.prop,
          label: col.label,
          message: errorMsg,
          row
        })
        if (!rowErrors.value[rowId]) rowErrors.value[rowId] = {}
        rowErrors.value[rowId][col.prop] = errorMsg
      } else {
        if (rowErrors.value[rowId]) {
          rowErrors.value[rowId][col.prop] = ''
        }
      }
    }
    allValid.push(rowValid)
  }

  const valid = allValid.every(v => v === true)
  return { valid, errors }
}

async function validateAndShow() {
  const { valid, errors } = validateAll()
  if (!valid && errors.length > 0) {
    const firstError = errors[0]
    ElMessage.warning(firstError.message)
    return false
  }
  return true
}

// ==================== 远程加载 ====================
async function loadDictOptions(dictType) {
  const dictStore = useDictStore()
  let options = dictStore.getDict(dictType)
  if (options && options.length > 0) return options
  try {
    const resp = await getDicts(dictType)
    options = resp.data.map(p => ({ label: p.itemLabel, value: p.itemValue, raw: p }))
    dictStore.setDict(dictType, options)
    return options
  } catch (err) {
    ElMessage.error(`字典 ${dictType} 加载失败`)
    return []
  }
}

async function loadRemoteOptions(col) {
  const { prop, api, requestParams } = col
  if (!api) return
  if (remoteOptions.value[prop]?.length > 0) return
  try {
    remoteLoading.value[prop] = true
    const payload = {
      ...(typeof requestParams === 'function' ? requestParams() : requestParams || {})
    }
    const res = await request({ url: api, method: 'post', data: payload })
    const list =
      typeof col.transform === 'function' ? col.transform(res) : res.data?.records || res.data || []
    remoteOptions.value[prop] = list.map(i => ({
      value: String(i[col.valueKey || 'value']),
      label: i[col.labelKey || 'label'],
      raw: i
    }))
  } catch (err) {
    ElMessage.error(`加载 ${col.label} 失败`)
    remoteOptions.value[prop] = []
  } finally {
    remoteLoading.value[prop] = false
  }
}

let searchTimeouts = {}
async function handleRemoteSearch(keyword, col) {
  const { prop, api, requestParams, keywordField = 'keyword', debounceWait = 300 } = col
  if (!api) return
  clearTimeout(searchTimeouts[prop])
  searchTimeouts[prop] = setTimeout(async () => {
    try {
      remoteLoading.value[prop] = true
      const payload = {
        ...(typeof requestParams === 'function' ? requestParams() : requestParams || {}),
        [keywordField]: keyword
      }
      const res = await request({ url: api, method: 'post', data: payload })
      const list =
        typeof col.transform === 'function'
          ? col.transform(res)
          : res.data?.records || res.data || []
      remoteOptions.value[prop] = list.map(i => ({
        value: String(i[col.valueKey || 'value']),
        label: i[col.labelKey || 'label'],
        raw: i
      }))
    } catch (err) {
      ElMessage.error(`搜索 ${col.label} 失败`)
    } finally {
      remoteLoading.value[prop] = false
    }
  }, debounceWait)
}

function getDisabledDateFn(col) {
  const { minDate, maxDate } = col
  if (minDate == null && maxDate == null) return () => false
  return date => {
    const current = dayjs(date).startOf('day')
    let minD = null,
      maxD = null
    if (minDate != null) {
      const val = typeof minDate === 'function' ? minDate() : minDate
      minD = val ? dayjs(val).startOf('day') : null
    }
    if (maxDate != null) {
      const val = typeof maxDate === 'function' ? maxDate() : maxDate
      maxD = val ? dayjs(val).startOf('day') : null
    }
    return (minD && current.isBefore(minD)) || (maxD && current.isAfter(maxD))
  }
}

// ==================== 上传相关 ====================
function getFileListDisplay(val) {
  if (!val) return []
  if (Array.isArray(val)) return val
  try {
    return JSON.parse(val)
  } catch {
    return [{ url: val, name: '文件' }]
  }
}

function beforeUpload(file, col) {
  if (col.maxSize && file.size > col.maxSize) {
    ElMessage.error(`文件 ${file.name} 超出大小限制`)
    return false
  }
  if (col.accept) {
    const ext = '.' + file.name.split('.').pop().toLowerCase()
    const allowed = col.accept.split(',').map(s => s.trim().toLowerCase())
    if (!allowed.includes(ext)) {
      ElMessage.error(`文件类型不支持，仅支持：${col.accept}`)
      return false
    }
  }
  return true
}

function handleUploadSuccess(row, col, response, file, fileList) {
  const data = response.data || response
  const url = data.url || data.fileUrl || data.path
  const name = data.name || data.fileName || file.name
  if (!url) {
    ElMessage.error('上传成功但未返回文件地址')
    return
  }
  const currentList = getFileListDisplay(row[col.prop])
  const newFile = { uid: file.uid, url, name }
  const newList = [...currentList, newFile]

  const rowIndex = props.tableData.findIndex(r => r[props.rowIdKey] === row[props.rowIdKey])
  if (rowIndex !== -1) {
    const newTableData = [...props.tableData]
    newTableData[rowIndex] = { ...row, [col.prop]: newList }
    emit('update:tableData', newTableData)
  }
  ElMessage.success(`${file.name} 上传成功`)
  emit('uploadSuccess', col.prop, newList)
}

function handleUploadError(row, col, error, file, fileList) {
  ElMessage.error(`文件 ${file.name} 上传失败`)
}

function removeFile(row, col, index) {
  const currentList = getFileListDisplay(row[col.prop])
  const newList = [...currentList]
  newList.splice(index, 1)

  const rowIndex = props.tableData.findIndex(r => r[props.rowIdKey] === row[props.rowIdKey])
  if (rowIndex !== -1) {
    const newTableData = [...props.tableData]
    newTableData[rowIndex] = { ...row, [col.prop]: newList }
    emit('update:tableData', newTableData)
  }
}

function previewFile(file) {
  const url = file.url
  if (!url) return
  const ext = (file.name || '').split('.').pop().toLowerCase()
  previewUrl.value = url
  previewFileName.value = file.name || '文件'
  if (['jpg', 'jpeg', 'png', 'gif', 'webp'].includes(ext)) {
    previewFileType.value = 'image'
    previewDialogVisible.value = true
  } else if (ext === 'pdf') {
    previewFileType.value = 'pdf'
    previewDialogVisible.value = true
  } else {
    window.open(url, '_blank')
  }
}

// ==================== 行操作 ====================
function handleDelete(row) {
  emit('delete', row)
}
//新增表格数据
const addTableData = () => {
  emit('add')
}
// ==================== 分页处理 ====================
function handleSizeChange(val) {
  pageSize.value = val
  emit('update:limit', val)
  emit('update:page', currentPage.value)
}

function handleCurrentChange(val) {
  currentPage.value = val
  emit('update:page', val)
}

watch(
  () => props.page,
  val => {
    currentPage.value = val
  }
)
watch(
  () => props.limit,
  val => {
    pageSize.value = val
  }
)

// ==================== 初始化 ====================
onMounted(async () => {
  for (const col of props.columns) {
    if (col.editType === 'select') {
      if (col.dictType) {
        const opts = await loadDictOptions(col.dictType)
        remoteOptions.value[col.prop] = opts
        dictLoaded.value.add(col.prop)
      } else if (col.api && col.preload !== false) {
        await loadRemoteOptions(col)
      } else if (col.options) {
        remoteOptions.value[col.prop] = [...col.options]
      }
    }
  }
})

// 暴露方法给父组件
defineExpose({
  validateAll,
  validateAndShow,
  refreshRemoteOptions: async prop => {
    const col = props.columns.find(c => c.prop === prop)
    if (col && col.api) {
      remoteOptions.value[prop] = []
      await loadRemoteOptions(col)
    }
  },
  clearErrors: () => {
    rowErrors.value = {}
  }
})
</script>

<style scoped>
.common-table {
  width: 100%;
}
.addBox {
  margin-bottom: 10px;
}
.table-pagination {
  margin-top: 16px;
  display: flex;
  justify-content: flex-end;
}
.operation-buttons {
  display: flex;
  gap: 8px;
  justify-content: center;
}
.table-upload-wrapper {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.upload-file-list {
  max-height: 80px;
  overflow-y: auto;
  border: 1px solid #dcdfe6;
  border-radius: 4px;
  padding: 4px 8px;
}
.file-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-size: 12px;
  padding: 2px 0;
}
.file-name {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.preview-container {
  text-align: center;
}
.preview-unsupported {
  padding: 40px;
  color: #909399;
}
.required-star {
  color: #f56c6c;
  margin-left: 2px;
}
</style>
