<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%"
      row-key="rowKey"
      v-bind="$attrs"
    >
      <!-- 树形模式：展开列 -->
      <el-table-column v-if="treeMode" type="expand" width="50">
        <template #default="{ row, $index }">
          <div class="sub-table-wrapper">
            <div class="sub-table-header">
              <span class="sub-table-title">{{ subTableTitle }}</span>
              <el-button type="primary" size="small" @click="addSubRow(row, $index)">
                {{ subTableAddBtnText }}
              </el-button>
            </div>
            <el-table
              :data="getSubData(row)"
              border
              size="small"
              class="sub-table"
            >
              <!-- 动态子表列 -->
              <el-table-column
                v-for="subCol in subTableColumns"
                :key="subCol.prop"
                :prop="subCol.prop"
                :label="subCol.label"
                :width="subCol.width"
                :min-width="subCol.minWidth"
                :align="subCol.align || 'left'"
              >
                <template #default="{ row: subRow, $index: subIndex }">
                  <el-form-item
                    :rules="getSubColumnRules(subCol)"
                    :error="getSubRowError(row, $index, subIndex, subCol.prop)"
                    style="margin-bottom: 0"
                  >
                    <!-- 输入框 -->
                    <el-input
                      v-if="subCol.editType === 'input'"
                      v-model="subRow[subCol.prop]"
                      :placeholder="subCol.placeholder || `请输入${subCol.label}`"
                      :clearable="true"
                      :type="subCol.inputType === 'textarea' ? 'textarea' : 'text'"
                      :rows="subCol.rows || 2"
                      :disabled="subCol.disabled"
                      @blur="validateSubField(row, $index, subIndex, subCol.prop)"
                    />
                    <!-- 下拉选择 -->
                    <el-select
                      v-else-if="subCol.editType === 'select'"
                      v-model="subRow[subCol.prop]"
                      :placeholder="subCol.placeholder || `请选择${subCol.label}`"
                      :clearable="true"
                      filterable
                      :disabled="subCol.disabled"
                      @change="validateSubField(row, $index, subIndex, subCol.prop)"
                    >
                      <el-option
                        v-for="opt in getSubSelectOptions(subCol)"
                        :key="opt.value"
                        :label="opt.label"
                        :value="opt.value"
                      />
                    </el-select>
                    <!-- 日期选择器 -->
                    <el-date-picker
                      v-else-if="subCol.editType === 'date'"
                      v-model="subRow[subCol.prop]"
                      type="date"
                      :placeholder="`选择${subCol.label}`"
                      :value-format="subCol.valueFormat || 'YYYY-MM-DD'"
                      style="width: 100%"
                      :disabled="subCol.disabled"
                    />
                    <!-- 月份选择器 -->
                    <el-date-picker
                      v-else-if="subCol.editType === 'month'"
                      v-model="subRow[subCol.prop]"
                      type="month"
                      :placeholder="`选择${subCol.label}`"
                      :value-format="subCol.valueFormat || 'YYYY-MM'"
                      style="width: 100%"
                      :disabled="subCol.disabled"
                    />
                    <!-- 日期范围 -->
                    <el-date-picker
                      v-else-if="subCol.editType === 'daterange'"
                      v-model="subRow[subCol.prop]"
                      type="daterange"
                      range-separator="至"
                      :start-placeholder="subCol.startPlaceholder || '开始日期'"
                      :end-placeholder="subCol.endPlaceholder || '结束日期'"
                      :value-format="subCol.valueFormat || 'YYYY-MM-DD'"
                      style="width: 100%"
                      :disabled="subCol.disabled"
                    />
                    <!-- 月份范围 -->
                    <el-date-picker
                      v-else-if="subCol.editType === 'monthrange'"
                      v-model="subRow[subCol.prop]"
                      type="monthrange"
                      range-separator="至"
                      :start-placeholder="subCol.startPlaceholder || '开始月份'"
                      :end-placeholder="subCol.endPlaceholder || '结束月份'"
                      :value-format="subCol.valueFormat || 'YYYY-MM'"
                      style="width: 100%"
                      :disabled="subCol.disabled"
                    />
                    <!-- 普通文本（不可编辑） -->
                    <span v-else>{{ subRow[subCol.prop] || '-' }}</span>
                  </el-form-item>
                </template>
              </el-table-column>

              <!-- 子表操作列 -->
              <el-table-column label="操作" width="100" align="center" fixed="right">
                <template #default="{ $index: subIndex }">
                  <el-button link type="danger" size="small" @click="deleteSubRow(row, $index, subIndex)">
                    删除
                  </el-button>
                </template>
              </el-table-column>
            </el-table>
            <div v-if="!getSubData(row).length" class="sub-table-empty">暂无明细数据，点击上方按钮添加</div>
          </div>
        </template>
      </el-table-column>

      <!-- 动态列（主表） -->
      <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">
            <!-- 树形模式：添加子行按钮 -->
            <el-button
              v-if="treeMode && showAddChild"
              link
              type="primary"
              size="small"
              @click="addSubRow(row, $index)"
            >
              {{ addChildBtnText }}
            </el-button>
            <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({
  // 基础 props
  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: '新增' },

  // 树形模式新增 props
  treeMode: { type: Boolean, default: false },
  subDataProp: { type: String, default: 'children' },
  subTableColumns: { type: Array, default: () => [] },
  subTableTitle: { type: String, default: '明细信息' },
  subTableAddBtnText: { type: String, default: '添加明细' },
  showAddChild: { type: Boolean, default: true },
  addChildBtnText: { type: String, default: '增加孩子' }
})

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

// ==================== 内部状态 ====================
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 editingTempValue = ref({})

// 子表格错误信息
const subRowErrors = 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
}

// ==================== 数字输入格式化与解析 ====================
function getInputDisplayValue(row, col) {
  const rowId = row[props.rowIdKey]
  const editingKey = `${rowId}_${col.prop}`
  if (editingTempValue.value[editingKey] !== undefined) {
    return editingTempValue.value[editingKey]
  }

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

  if (!['integer', 'decimalNumber', 'decimal'].includes(inputType)) {
    return rawValue ?? ''
  }

  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('.')
}

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 {
    filtered = rawValue
  }

  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 {
    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 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)
}

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 getSubData(row) {
  return row[props.subDataProp] || []
}

function getSubColumnRules(col) {
  if (col.rules) return col.rules
  if (col.required) {
    return [{ required: true, message: `${col.label}不能为空`, trigger: 'blur' }]
  }
  return []
}

function getSubSelectOptions(col) {
  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.options) {
    return col.options.map(opt => ({ value: opt.value, label: opt.label, raw: opt.raw }))
  }
  return []
}

function getSubRowError(mainRow, mainRowIndex, subIndex, prop) {
  const rowId = mainRow[props.rowIdKey] || mainRowIndex
  return subRowErrors.value[rowId]?.[subIndex]?.[prop] || ''
}

async function validateSubField(mainRow, mainRowIndex, subIndex, prop) {
  const subCol = props.subTableColumns.find(c => c.prop === prop)
  if (!subCol) return true

  const rules = getSubColumnRules(subCol)
  if (!rules.length) return true

  const subData = getSubData(mainRow)
  const subRow = subData[subIndex]
  if (!subRow) return true

  const value = subRow[prop]
  const rowId = mainRow[props.rowIdKey] || mainRowIndex
  let errorMsg = ''

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

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

function addSubRow(mainRow, mainRowIndex) {
  const newSubRow = {}
  for (const col of props.subTableColumns) {
    if (col.defaultValue !== undefined) {
      newSubRow[col.prop] = typeof col.defaultValue === 'function' ? col.defaultValue() : col.defaultValue
    } else {
      newSubRow[col.prop] = null
    }
  }
  newSubRow._tempId = Date.now() + Math.random()

  const currentSubData = getSubData(mainRow)
  const newSubData = [...currentSubData, newSubRow]

  const newTableData = [...props.tableData]
  newTableData[mainRowIndex] = {
    ...mainRow,
    [props.subDataProp]: newSubData
  }
  emit('update:tableData', newTableData)
  emit('sub-row-add', { mainRow, mainRowIndex, newRow: newSubRow })
}

function deleteSubRow(mainRow, mainRowIndex, subIndex) {
  const currentSubData = getSubData(mainRow)
  const newSubData = [...currentSubData]
  const deletedRow = newSubData.splice(subIndex, 1)[0]

  const newTableData = [...props.tableData]
  newTableData[mainRowIndex] = {
    ...mainRow,
    [props.subDataProp]: newSubData
  }
  emit('update:tableData', newTableData)
  emit('sub-row-delete', { mainRow, mainRowIndex, subIndex, deletedRow })

  const rowId = mainRow[props.rowIdKey] || mainRowIndex
  if (subRowErrors.value[rowId]) {
    delete subRowErrors.value[rowId][subIndex]
    const newErrors = {}
    for (let i = 0; i < newSubData.length; i++) {
      if (subRowErrors.value[rowId][i] !== undefined) {
        newErrors[i] = subRowErrors.value[rowId][i]
      }
    }
    subRowErrors.value[rowId] = newErrors
  }
}

// ==================== 全局校验方法 ====================
function validateAll() {
  const errors = []
  let allValid = true

  // 校验主表
  for (let i = 0; i < props.tableData.length; i++) {
    const row = props.tableData[i]
    const rowId = row[props.rowIdKey] || i

    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) {
        allValid = false
        errors.push({
          type: 'main',
          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] = ''
      }
    }

    // 校验子表格
    if (props.treeMode) {
      const subData = getSubData(row)
      for (let j = 0; j < subData.length; j++) {
        const subRow = subData[j]
        for (const subCol of props.subTableColumns) {
          const rules = getSubColumnRules(subCol)
          if (rules.length === 0) continue

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

          if (errorMsg) {
            allValid = false
            errors.push({
              type: 'sub',
              mainRowIndex: i,
              subIndex: j,
              prop: subCol.prop,
              label: subCol.label,
              message: errorMsg,
              row: subRow
            })
            if (!subRowErrors.value[rowId]) subRowErrors.value[rowId] = {}
            if (!subRowErrors.value[rowId][j]) subRowErrors.value[rowId][j] = {}
            subRowErrors.value[rowId][j][subCol.prop] = errorMsg
          } else if (subRowErrors.value[rowId]?.[j]?.[subCol.prop]) {
            subRowErrors.value[rowId][j][subCol.prop] = ''
          }
        }
      }
    }
  }

  return { valid: allValid, errors }
}

async function validateAndShow() {
  const { valid, errors } = validateAll()
  if (!valid && errors.length > 0) {
    ElMessage.warning(errors[0].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)
}

function 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 = {}
    subRowErrors.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;
}
.sub-table-wrapper {
  padding: 12px 20px;
  background-color: #fafafa;
  border-radius: 4px;
}
.sub-table-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 12px;
}
.sub-table-title {
  font-weight: 500;
  color: #303133;
}
.sub-table {
  width: 100%;
}
.sub-table-empty {
  text-align: center;
  padding: 20px;
  color: #909399;
  background-color: #fff;
  border: 1px dashed #dcdfe6;
  border-radius: 4px;
  margin-top: 8px;
}
</style>
<!--
使用实例：
<treeEditTable
        :tableData="addSalaryData"
        :columns="mainColumns"
        :treeMode="true"
        :subDataProp="'bankDetails'"
        :subTableColumns="subColumns"
        subTableTitle="收款银行明细"
        subTableAddBtnText="添加收款账户"
        addChildBtnText="增加孩子"
        @update:tableData="handleAddSalaryDataUpdate"
      ></treeEditTable>
      const mainColumns = [
  { prop: 'name', label: '姓名', editType: 'input', required: true },
  { prop: 'internalNo', label: '内部编号', editType: 'input' },
  { prop: 'salaryMonth', label: '薪资月份', editType: 'month' },
  { prop: 'amount', label: '应发金额', editType: 'input', inputType: 'decimal' }
  // ... 其他主表列
]

const subColumns = [
  {
    prop: 'bankName',
    label: '收款银行',
    editType: 'select',
    options: [{ value: 'ICBC', label: '工商银行' }],
    required: true
  },
  { prop: 'accountTail', label: '账户尾号', editType: 'input', required: true },
  {
    prop: 'currency',
    label: '币种',
    editType: 'select',
    options: [{ value: 'CNY', label: '人民币' }]
  },
  { prop: 'payAmount', label: '发放金额', editType: 'input', inputType: 'decimal' },
  { prop: 'exchangeRate', label: '汇率', editType: 'input', inputType: 'decimal' },
  { prop: 'remark', label: '备注', editType: 'input' }
]

const addSalaryData = ref([
  {
    id: 1,
    name: '张三',
    internalNo: '001',
    salaryMonth: '2025-06',
    amount: 10000,
    bankDetails: [
      {
        bankName: 'ICBC',
        accountTail: '1234',
        currency: 'CNY',
        payAmount: 5000,
        exchangeRate: 1,
        remark: ''
      },
      {
        bankName: 'ICBC',
        accountTail: '5678',
        currency: 'CNY',
        payAmount: 5000,
        exchangeRate: 1,
        remark: ''
      }
    ]
  }
])
 -->
