<!-- searchForm -->
<template>
  <div>
    <el-form
      ref="formRef"
      :model="localModel"
      :rules="formRules"
      label-width="auto"
      v-bind="$attrs"
      :validate-on-rule-change="false"
    >
      <el-row :gutter="20">
        <el-col v-for="item in visibleConfig" :key="item.prop" :span="item.span || 6">
          <el-form-item
            :label="item.label"
            :prop="item.prop"
            :class="{ 'search-form-item': isSearch }"
            :label-position="item.labelPosition || 'top'"
          >
            <!-- Input -->
            <el-input
              v-if="item.type === 'input'"
              v-model="localModel[item.prop]"
              :placeholder="item.placeholder || `请输入${item.label}`"
              :clearable="true"
              :disabled="item.disabled"
              @input="val => handleNumberInput(val, item)"
              @change="val => handleModelChange(val, item)"
              :formatter="
                value => {
                  if (!item.inputType || !value) return value
                  const str = String(value)
                  // 如果包含小数点
                  if (str.indexOf('.') > -1) {
                    const [int, dec] = str.split('.')
                    // 只格式化整数部分，保留小数部分原样
                    return int.replace(/\B(?=(\d{3})+(?!\d))/g, ',') + '.' + dec
                  }
                  // 如果没有小数点，直接格式化
                  return str.replace(/\B(?=(\d{3})+(?!\d))/g, ',')
                }
              "
              :parser="value => value.replace(/\$\s?|(,*)/g, '')"
            />

            <!-- Select (支持 dictType / api / options) -->
            <el-select
              v-else-if="item.type === 'select'"
              v-model="localModel[item.prop]"
              :multiple="!!item.multiple"
              :placeholder="item.placeholder || `请选择${item.label}`"
              :clearable="true"
              filterable
              :disabled="item.disabled"
              :loading="remoteLoading[item.prop] || false"
              @change="val => handleModelChange(val, item)"
              @focus="() => loadRemoteOptions(item)"
              @filter-change="keyword => handleFilterChange(keyword, item)"
            >
              <el-option
                v-for="opt in getSelectOptions(item)"
                :key="opt.value"
                :label="opt.label"
                :value="opt.value"
              />
            </el-select>
            <!-- Date -->
            <el-date-picker
              v-else-if="item.type === 'date'"
              v-model="localModel[item.prop]"
              type="date"
              :placeholder="`选择${item.label}`"
              :disabled="item.disabled"
              :value-format="item.valueFormat || 'YYYY-MM-DD'"
              style="width: 100%"
              :disabled-date="getDisabledDateFn(item)"
              @change="val => handleModelChange(val, item)"
            />
            <!-- Month -->
            <el-date-picker
              v-else-if="item.type === 'month'"
              v-model="localModel[item.prop]"
              type="month"
              :placeholder="`选择${item.label}`"
              :value-format="item.valueFormat || 'YYYY-MM'"
              style="width: 100%"
              :disabled="item.disabled"
              :disabled-date="getDisabledDateFn(item)"
              @change="val => handleModelChange(val, item)"
            />

            <!-- Daterange -->
            <el-date-picker
              v-else-if="item.type === 'daterange'"
              v-model="localModel[item.prop]"
              type="daterange"
              range-separator="至"
              :start-placeholder="item.startPlaceholder || '开始日期'"
              :end-placeholder="item.endPlaceholder || '结束日期'"
              :value-format="item.valueFormat || 'YYYY-MM-DD'"
              :disabled="item.disabled"
              :disabled-date="getDisabledDateFn(item)"
              style="width: 100%"
              @change="val => handleModelChange(val, item)"
            />
            <!-- Monthrange (新增) -->
            <el-date-picker
              v-else-if="item.type === 'monthrange'"
              v-model="localModel[item.prop]"
              type="monthrange"
              range-separator="至"
              :start-placeholder="item.startPlaceholder || '开始月份'"
              :end-placeholder="item.endPlaceholder || '结束月份'"
              :value-format="item.valueFormat || 'YYYY-MM'"
              :disabled="item.disabled"
              :disabled-date="getDisabledDateFn(item)"
              style="width: 100%"
              @change="val => handleModelChange(val, item)"
            />

            <!-- Checkbox Group -->
            <el-checkbox-group
              v-else-if="item.type === 'checkbox-group'"
              v-model="localModel[item.prop]"
              :disabled="item.disabled"
              @change="val => handleModelChange(val, item)"
            >
              <el-checkbox
                v-for="opt in getSelectOptions(item)"
                :key="opt.value"
                :label="opt.value"
              >
                {{ opt.label }}
              </el-checkbox>
            </el-checkbox-group>
            <!-- textarea -->
            <el-input
              v-else-if="item.type === 'textarea'"
              v-model="localModel[item.prop]"
              style="width: 240px"
              autosize
              :disabled="item.disabled"
              type="textarea"
              placeholder="请输入"
              :clearable="true"
              @change="val => handleModelChange(val, item)"
            />
            <!-- Upload 回显值得时候数据格式至少是[{url: '必须要传', name: 'name不是必须的根据需要传值'}]-->
            <!-- <el-upload
              v-else-if="item.type === 'upload'"
              v-model:file-list="localModel[item.prop]"
              :action="item.action"
              :headers="item.headers"
              :multiple="!!item.multiple"
              :limit="item.limit || (item.multiple ? 999 : 1)"
              :accept="item.accept"
              :list-type="item.listType || 'text'"
              :disabled="item.disabled"
              :auto-upload="true"
              :show-file-list="item.showFileList"
              :on-exceed="handleExceed"
              :before-upload="file => beforeUpload(file, item)"
              :on-success="(res, file, fileList) => handleUploadSuccess(res, file, fileList, item)"
              :on-error="(err, file, fileList) => handleUploadError(err, file, fileList, item)"
              :on-remove="(file, fileList) => handleUploadRemove(file, fileList, item)"
            >
              <el-icon class="iconStyle" :size="20" v-if="item.uploadType === 'image'">
                <Upload />
              </el-icon>
              <el-button
                v-else
                size="small"
                type="primary"
                :link="item.link"
                :disabled="item.disabled"
              >
                {{ '点击上传文件' }}
              </el-button>
              <template #tip v-if="item.maxSize || item.accept">
                <div class="el-upload__tip">
                  <span v-if="item.maxSize">大小不超过 {{ formatFileSize(item.maxSize) }}</span>
                  <span v-if="item.accept">支持格式：{{ item.accept }}</span>
                </div>
              </template>
            </el-upload> -->
            <!-- Upload 回显值时数据格式至少是 [{url: '必须', name: '可选'}] -->
            <template v-else-if="item.type === 'upload'">
              <!-- 🔽 默认模式：完整使用 el-upload（含自带文件列表） -->
              <el-upload
                v-if="!item.customFileList"
                v-model:file-list="localModel[item.prop]"
                :action="item.action"
                :headers="item.headers"
                :multiple="!!item.multiple"
                :limit="item.limit || (item.multiple ? 999 : 1)"
                :accept="item.accept"
                :list-type="item.listType || 'text'"
                :disabled="item.disabled"
                :auto-upload="true"
                :show-file-list="item.showFileList !== false"
                :on-exceed="handleExceed"
                :before-upload="file => beforeUpload(file, item)"
                :on-success="
                  (res, file, fileList) => handleUploadSuccess(res, file, fileList, item)
                "
                :on-error="(err, file, fileList) => handleUploadError(err, file, fileList, item)"
                :on-remove="(file, fileList) => handleUploadRemove(file, fileList, item)"
              >
                <el-icon class="iconStyle" :size="20" v-if="item.uploadType === 'image'">
                  <Upload />
                </el-icon>
                <el-button
                  v-else
                  size="small"
                  type="primary"
                  :link="item.link"
                  :disabled="item.disabled"
                >
                  点击上传文件
                </el-button>
                <template #tip v-if="item.maxSize || item.accept">
                  <div class="el-upload__tip">
                    <span v-if="item.maxSize">大小不超过 {{ formatFileSize(item.maxSize) }}</span>
                    <span v-if="item.accept">支持格式：{{ item.accept }}</span>
                  </div>
                </template>
              </el-upload>

              <!-- 🔽 自定义列表模式：只保留上传触发器，自己渲染文件列表 -->
              <div v-else class="custom-upload-wrapper">
                <el-upload
                  v-model:file-list="localModel[item.prop]"
                  :action="item.action"
                  :headers="item.headers"
                  :multiple="!!item.multiple"
                  :limit="item.limit || (item.multiple ? 999 : 1)"
                  :accept="item.accept"
                  :disabled="item.disabled"
                  :auto-upload="true"
                  :show-file-list="false"
                  :on-exceed="handleExceed"
                  :before-upload="file => beforeUpload(file, item)"
                  :on-success="
                    (res, file, fileList) => handleUploadSuccess(res, file, fileList, item)
                  "
                  :on-error="(err, file, fileList) => handleUploadError(err, file, fileList, item)"
                  :on-remove="(file, fileList) => handleUploadRemove(file, fileList, item)"
                >
                  <el-button type="primary" size="small" :disabled="item.disabled">
                    {{ item.customUploadButtonText || '点击上传文件' }}
                  </el-button>
                  <template #tip v-if="item.maxSize || item.accept">
                    <div class="el-upload__tip">
                      <span v-if="item.maxSize">大小不超过 {{ formatFileSize(item.maxSize) }}</span>
                      <span v-if="item.accept">支持格式：{{ item.accept }}</span>
                    </div>
                  </template>
                </el-upload>

                <!-- 自定义文件列表 -->
                <div v-if="(localModel[item.prop] || []).length" class="custom-file-list">
                  <div
                    v-for="(file, idx) in localModel[item.prop]"
                    :key="file.uid || idx"
                    class="file-item"
                  >
                    <span class="file-name" :title="file.name">{{ file.name }}</span>
                    <div class="file-actions">
                      <el-button link type="primary" size="small" @click="previewFile(file, item)">
                        查看
                      </el-button>
                      <el-button link type="danger" size="small" @click="removeFile(file, item)">
                        删除
                      </el-button>
                    </div>
                  </div>
                </div>
              </div>
            </template>
            <span v-else>不支持的类型: {{ item.type }}</span>
          </el-form-item>
        </el-col>
      </el-row>
    </el-form>
    <!-- 文件预览弹窗（页面内查看，不打开新窗口） -->
    <el-dialog
      v-model="previewDialogVisible"
      :title="previewFileName"
      width="70%"
      :close-on-click-modal="false"
      destroy-on-close
      @close="previewUrl = ''"
    >
      <div class="preview-container">
        <!-- 图片预览 -->
        <div v-if="previewFileType === 'image'" class="preview-image-wrapper">
          <img :src="previewUrl" class="preview-image" alt="预览图片" />
        </div>

        <!-- PDF 预览（使用 iframe 内嵌） -->
        <iframe
          v-else-if="previewFileType === 'pdf'"
          :src="previewUrl"
          class="preview-pdf"
          frameborder="0"
        ></iframe>

        <!-- 不支持预览的文件类型 -->
        <div v-else-if="previewFileType === 'unsupported'" class="preview-unsupported">
          <el-icon :size="48" color="#909399"><Document /></el-icon>
          <p>暂不支持预览此类型文件</p>
          <el-button type="primary" @click="previewDialogVisible = false"> 关闭 </el-button>
        </div>
      </div>
    </el-dialog>
  </div>
</template>

<script setup>
import { ref, watch, onMounted, nextTick, computed } from 'vue'
import { ElMessage } from 'element-plus'
import { Upload, Document } from '@element-plus/icons-vue'
// 🔑 引入你的字典方法
import useDictStore from '@/store/modules/dict'
import { getDicts } from '@/api/system/dict/data'
import request from '@/utils/request'
import dayjs from 'dayjs'
// ==================== 上传文件 ====================
// ==================== 文件预览弹窗 ====================
const previewDialogVisible = ref(false)
const previewUrl = ref('')
const previewFileName = ref('')
const previewFileType = ref('') // 'image', 'pdf', 'unsupported'
//新增文件上传自定义方法开始
// 预览文件（支持图片和PDF）

// 预览文件（页面内弹窗，不打开新窗口）
function previewFile(file, item) {
  const url = file.url || file.response?.data?.url
  if (!url) {
    ElMessage.warning('文件地址不存在')
    return
  }
  const ext = (file.name || '').split('.').pop().toLowerCase()
  previewUrl.value = url
  previewFileName.value = file.name || '文件'

  if (['jpg', 'jpeg', 'png', 'webp', 'gif', 'bmp', 'svg'].includes(ext)) {
    previewFileType.value = 'image'
    previewDialogVisible.value = true
  } else if (ext === 'pdf') {
    previewFileType.value = 'pdf'
    previewDialogVisible.value = true
  } else {
    // 不支持预览的文件类型，弹窗显示提示
    previewFileType.value = 'unsupported'
    previewDialogVisible.value = true
  }
}

// 删除文件（复用原有删除逻辑）
function removeFile(file, item) {
  const fileList = localModel.value[item.prop] || []
  const newList = fileList.filter(f => f.uid !== file.uid)
  handleUploadRemove(file, newList, item) // 调用原有的删除处理函数
}
//新增文件上传自定义方法结束

// 文件大小格式化
function formatFileSize(bytes) {
  if (bytes === 0) return '0 Bytes'
  const k = 1024
  const sizes = ['Bytes', 'KB', 'MB', 'GB']
  const i = Math.floor(Math.log(bytes) / Math.log(k))
  return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
}
function beforeUpload(file, item) {
  // 检查文件大小
  if (item.maxSize && file.size > item.maxSize) {
    ElMessage.error(`文件 ${file.name} 超出大小限制（最大 ${formatFileSize(item.maxSize)}）`)
    return false
  }

  // 检查类型（accept 已由浏览器限制，但可二次校验）
  if (item.accept) {
    const allowed = item.accept.split(',').map(ext => ext.trim().toLowerCase())
    const fileExt = '.' + file.name.split('.').pop().toLowerCase()
    if (!allowed.includes(fileExt)) {
      ElMessage.error(`文件类型不支持，仅支持：${item.accept}`)
      return false
    }
  }

  return true
}
function handleUploadSuccess(response, file, fileList, item) {
  // 假设你的后端返回格式：{ code: 200, data: { url: '...', name: 'xxx.pdf' } }
  // 你可以通过 item.responseMap 自定义映射，这里先用通用方式

  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
  }

  // 找到刚上传的文件（通过 uid），替换其 url
  const targetFile = fileList.find(f => f.uid === file.uid)
  if (targetFile) {
    targetFile.url = url
    targetFile.name = name
  }

  // 触发 model 更新
  handleModelChange([...fileList], item)
  ElMessage.success(`文件 ${file.name} 上传成功`)

  // console.log('上传成功', item)
}
function handleExceed(files, fileList) {
  ElMessage.warning('超出文件数量限制')
}

function handleUploadError(error, file, fileList, item) {
  ElMessage.error(`文件 ${file.name} 上传失败`)
  console.error('Upload error:', error)
}

function handleUploadRemove(file, fileList, item) {
  // 用户删除文件时，同步更新 model
  handleModelChange([...fileList], item)
}
// ==================== 工具函数：深拷贝配置（保留函数） ====================
function deepCloneConfig(obj) {
  if (obj === null || typeof obj !== 'object') return obj
  if (Array.isArray(obj)) return obj.map(deepCloneConfig)
  const cloned = {}
  for (const key in obj) {
    if (Object.prototype.hasOwnProperty.call(obj, key)) {
      const val = obj[key]
      cloned[key] = typeof val === 'function' ? val : deepCloneConfig(val)
    }
  }
  return cloned
}
function parseToDate(str) {
  if (!str) return null
  if (str === 'today') {
    return dayjs().startOf('day')
  }
  if (typeof str === 'string') {
    const d = dayjs(str)
    return d.isValid() ? d.startOf('day') : null
  }
  if (str instanceof Date) {
    return dayjs(str).startOf('day')
  }
  return null
}
// ==================== 生成 disabledDate 函数 ====================
function getDisabledDateFn(item) {
  const { minDate, maxDate } = item

  // 如果都没有限制，返回 null（不禁用任何日期）
  if (minDate == null && maxDate == null) {
    return () => false
  }

  return date => {
    const currentDate = dayjs(date).startOf('day')
    let minD = null
    let maxD = null

    // 解析最小日期
    if (minDate != null) {
      if (typeof minDate === 'function') {
        const val = minDate(localModel.value)
        minD = parseToDate(val)
      } else {
        minD = parseToDate(minDate)
      }
    }

    // 解析最大日期
    if (maxDate != null) {
      if (typeof maxDate === 'function') {
        const val = maxDate(localModel.value)
        maxD = parseToDate(val)
      } else {
        maxD = parseToDate(maxDate)
      }
    }

    // 判断是否被禁用
    if (minD && currentDate.isBefore(minD)) {
      return true
    }
    if (maxD && currentDate.isAfter(maxD)) {
      return true
    }
    return false
  }
}
// ==================== Props & Emits ====================
const props = defineProps({
  modelValue: { type: Object, default: () => ({}) },
  config: { type: Array, default: () => [] },
  isSearch: { type: Boolean, default: false }
})

const emit = defineEmits([
  'update:modelValue',
  'update',
  'selectChange',
  'uploadSuccess',
  'inputChange'
])

// ==================== Refs ====================
const formRef = ref(null)
// 使用 shallowRef 避免深层响应式（表单通常扁平）
const localModel = ref({ ...props.modelValue })
// 记录哪些字段的字典已加载
const dictLoaded = ref(new Set())
const internalConfig = ref([])
const remoteOptions = ref({}) // { prop: [options] }
const remoteLoading = ref({}) // { prop: boolean }

// ==================== 条件显隐 & 规则 ====================
const visibleConfig = computed(() => {
  return internalConfig.value.filter(item => {
    if (typeof item.visible === 'function') {
      return item.visible(localModel.value)
    }
    return true
  })
})

const formRules = computed(() => {
  const rules = {}
  visibleConfig.value.forEach(item => {
    if (item.rules) rules[item.prop] = item.rules
  })
  return rules
})

// 1. 外部 modelValue 变化时，安全同步（仅当内容不同时）
// 监听 config 变化（支持动态 config）
watch(
  () => props.config,
  newConfig => {
    if (!newConfig || newConfig.length === 0) return

    internalConfig.value = deepCloneConfig(newConfig)

    // 构建初始模型
    const initialModel = {}
    for (const item of internalConfig.value) {
      const key = item.prop
      // 优先用父传值，否则用默认值
      if (props.modelValue?.[key] !== undefined) {
        initialModel[key] = props.modelValue[key]
      } else if (
        item.multiple ||
        ['checkbox-group', 'daterange', 'monthrange'].includes(item.type)
      ) {
        initialModel[key] = item.defaultValue ?? []
      } else {
        initialModel[key] = item.defaultValue ?? ''
      }
    }

    // ✅ 在这里同步 modelValue（包括 extra 字段）
    localModel.value = syncModelFromProps(props.modelValue, internalConfig.value)
    console.log('子组件监测config变化', localModel.value)
  },
  { immediate: true }
)
// console.log('🚀 子组件 props.modelValue 初始值:', props.modelValue)

// 监听 modelValue（用于后续外部更新）
watch(
  () => props.modelValue,
  newVal => {
    if (!newVal || !internalConfig.value) return
    // ✅ 同样使用 sync 函数
    localModel.value = syncModelFromProps(newVal, internalConfig.value)
    console.log('子组件监测 modelValue 变化:', localModel.value)
  },
  { deep: true }
)

// 提取同步逻辑
function syncModelFromProps(newModelValue, newConfig) {
  if (!newModelValue || !newConfig) return {}

  const synced = {}
  // 1. 同步主字段
  for (const item of newConfig) {
    const key = item.prop
    if (newModelValue.hasOwnProperty(key)) {
      synced[key] = newModelValue[key]
    } else if (item.multiple || ['checkbox-group', 'daterange', 'monthrange'].includes(item.type)) {
      synced[key] = item.defaultValue ?? []
    } else {
      synced[key] = item.defaultValue ?? ''
    }
  }
  // 2. 同步 extra 字段（通过 options 反查）
  for (const item of newConfig) {
    const extraMap = item.onChangeExtraFields
    if (!extraMap || typeof extraMap !== 'object') continue

    const prop = item.prop
    const idValue = newModelValue[prop] // e.g. 2

    let sourceObj = null

    // 情况1: 如果 newModelValue[prop] 本身就是对象 → 直接用（兼容旧逻辑）
    if (idValue && typeof idValue === 'object') {
      sourceObj = idValue
    }
    // 情况2: 如果是 primitive（string/number），且有 options → 反查
    else if (Array.isArray(item.options) && idValue !== undefined && idValue !== null) {
      // 默认用 option.value 匹配，可配置 valueKey
      const valueKey = item.valueKey || 'value'
      sourceObj = item.options.find(opt => opt[valueKey] === idValue)
    }

    // 如果找到了 sourceObj，就提取 extra 字段
    if (sourceObj && typeof sourceObj === 'object') {
      for (const [targetKey, subPath] of Object.entries(extraMap)) {
        const val = getNestedValue(sourceObj, subPath)
        if (val !== undefined) {
          synced[targetKey] = val
        }
      }
    }
  }

  // 3. 保留 localModel 中的 extra 字段（仅当 newModelValue 中没有对应的 sourceField 时）
  for (const item of newConfig) {
    const sourceField = item.prop
    const extraMap = item.onChangeExtraFields
    if (!extraMap || typeof extraMap !== 'object') continue

    // 如果 newModelValue 中没有 sourceField，说明没有重新计算
    if (newModelValue[sourceField] === undefined) {
      // 那么保留 localModel 中对应的 extra 字段
      for (const [targetKey, subPath] of Object.entries(extraMap)) {
        if (localModel.value.hasOwnProperty(targetKey)) {
          synced[targetKey] = localModel.value[targetKey]
        }
      }
    }
  }

  // 4. 保留 newModelValue 中已有的 extra 字段和其他额外字段
  for (const key in newModelValue) {
    // 如果已经同步过了（比如主字段或第2步写入的extra），跳过
    if (synced.hasOwnProperty(key)) continue

    // 判断是否是某个 extra 目标字段
    const isExtraTarget = newConfig.some(
      item => item.onChangeExtraFields && item.onChangeExtraFields.hasOwnProperty(key)
    )

    // 如果是 extra 字段，且 newModelValue 里有值 → 保留它！
    if (isExtraTarget) {
      synced[key] = newModelValue[key]
    }
    // 如果不是 extra，也不是主字段 → 也保留（兼容 hidden 字段等）
    else if (!newConfig.some(item => item.prop === key)) {
      synced[key] = newModelValue[key]
    }
  }
  console.log('🚀 子组件 进行modelvalue处理:', synced)
  return synced
}
function getNestedValue(obj, path) {
  return path.split('.').reduce((current, key) => current?.[key], obj)
}
// 当字典加载完成时，触发同步
function markDictLoaded(prop) {
  dictLoaded.value.add(prop)
  // 尝试同步该字段
  if (props.modelValue?.[prop] !== undefined) {
    localModel.value[prop] = props.modelValue[prop]
  }
}
// 2. 用户操作导致 localModel 变化时，emit（防抖可选）

function handleModelChange(value, item) {
  console.group('用户操作导致 localModel 变化时，emit（防抖可选）')

  const newModel = { ...localModel.value, [item.prop]: value }

  if (item?.type === 'select' && item.onChangeExtraFields) {
    const options = getSelectOptions(item)
    // console.log('可用 options:', options)
    const opt = options.find(o => o.value === value)
    // console.log('匹配的 opt:', opt)
    if (opt?.raw) {
      for (const [targetProp, sourceKey] of Object.entries(item.onChangeExtraFields)) {
        const extraValue = opt.raw[sourceKey]
        newModel[targetProp] = extraValue
        // console.log(`✅ 设置 ${targetProp} =`, extraValue)
      }
    }
  }
  localModel.value = newModel
  // console.log('子组件用户操作后，modelvalue值==', newModel)
  nextTick(() => {
    if (!isEqualShallow(props.modelValue, newModel)) {
      // console.log('如果新旧值不一样，反馈给父组件', newModel)
      emit('update:modelValue', newModel)
    } else {
      console.log('🚫 跳过 emit：认为相等')
    }
  })
  if (item.type === 'select') {
    // console.log('如果是select类型，反馈给父组件', item.prop, value, item)
    emit('selectChange', item.prop, value, item)
  } else if (item.type == 'upload') {
    // 传给父组件最新的上传值newModel
    emit('uploadSuccess', item.prop, newModel)
  } else if (item.type == 'input') {
    emit('inputChange', item.prop, value, item)
  }
  console.groupEnd()
}
// 辅助函数：浅比较两个对象
function isEqualShallow(a, b) {
  const keysA = Object.keys(a)
  const keysB = Object.keys(b)
  if (keysA.length !== keysB.length) return false
  for (let key of keysA) {
    if (a[key] !== b[key]) 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) {
    // console.error(`加载字典 ${dictType} 失败`, err)
    ElMessage.error(`字典 ${dictType} 加载失败`)
    return []
  }
}

// ==================== 初始化 ====================
onMounted(async () => {
  internalConfig.value = deepCloneConfig(props.config)

  const initialData = {}
  const dictTypePromises = []
  const apiPromises = [] // ← 新增：收集 api 加载 promise
  for (const item of internalConfig.value) {
    const key = item.prop
    if (localModel.value[key] == null) {
      if (item.multiple) {
        initialData[key] = item.defaultValue ?? []
      } else if (['checkbox-group', 'daterange', 'monthrange'].includes(item.type)) {
        initialData[key] = item.defaultValue ?? []
      } else {
        initialData[key] = item.defaultValue ?? ''
      }
    }

    // 预加载 dictType
    if (item.type === 'select' && item.dictType) {
      dictTypePromises.push(
        loadDictOptions(item.dictType).then(opts => {
          remoteOptions.value[key] = opts
          markDictLoaded(key) // ← 立即标记已加载
        })
      )
    } // 预加载 api（远程接口）← 关键新增！
    else if (item.type === 'select' && item.api) {
      apiPromises.push(
        loadRemoteOptionsForInit(item) // ← 专门用于初始化的加载函数
      )
    } else if (item.type === 'select' && item.options) {
      remoteOptions.value[key] = [...item.options]
      markDictLoaded(key)
    }
    // api 类型：延迟加载（focus 时）
  }

  if (Object.keys(initialData).length > 0) {
    localModel.value = { ...localModel.value, ...initialData }
  }

  // 等待所有字典加载完成
  await Promise.allSettled(dictTypePromises)
})

// ==================== 获取 select 选项 ====================
function getSelectOptions(item) {
  const key = item.prop
  // 字典选项
  if (item.dictType || item.api) {
    // 字典选项
    return (remoteOptions.value[key] || []).map(opt => ({
      value: opt.value,
      label: opt.label,
      raw: opt.raw // ← 必须存 raw
    }))
  } else if (item.options) {
    // 静态选项s
    return item.options.map(opt => ({
      value: opt.value,
      label: opt.label,
      raw: opt.raw // 保留原始
    }))
  }
  return []
}
async function loadRemoteOptionsForInit(item) {
  const { prop, api, requestParams } = item
  try {
    // 构造请求体：只传 requestParams，不传 keyword
    const payload = {
      ...(typeof requestParams === 'function' ? requestParams() : requestParams || {})
    }

    const res = await request({
      url: api,
      method: 'post',
      data: payload
    })

    const list =
      typeof item.transform === 'function'
        ? item.transform(res)
        : res.data?.records || res.data || []

    // 建议：统一转成字符串（或根据 item.valueType 判断）
    const newOptions = list.map(i => ({
      value: String(i[item.valueKey || 'value']), // ← 强制转字符串
      label: i[item.labelKey || 'label'],
      raw: i
    }))

    remoteOptions.value[prop] = newOptions
    markDictLoaded(prop) // ← 关键：标记已加载
  } catch (err) {
    ElMessage.error(`预加载 ${item.label} 失败`)
    remoteOptions.value[prop] = []
  }
}
// ==================== 加载远程 API 选项（首次 focus 时加载，无搜索词） ====================
async function loadRemoteOptions(item) {
  const { prop, api, requestParams, keywordField, debounceWait, ...rest } = item
  if (!api) return

  try {
    remoteLoading.value[prop] = true

    // 构造请求体：合并 requestParams + 分页（默认第一页）
    const payload = {
      ...(typeof requestParams === 'function' ? requestParams() : requestParams || {})
      // 注意：此时无 keyword，所以不加 keywordField
    }

    const res = await request({
      url: api,
      method: 'post', // ← 改为 POST
      data: payload // ← 参数放 body
    })

    const list =
      typeof item.transform === 'function'
        ? item.transform(res)
        : res.data?.records || res.data || []

    // 建议：统一转成字符串（或根据 item.valueType 判断）
    const newOptions = list.map(i => ({
      value: String(i[item.valueKey || 'value']), // ← 强制转字符串
      label: i[item.labelKey || 'label'],
      raw: i
    }))
    remoteOptions.value[prop] = newOptions
    // ✅ 关键：标记该字段字典已加载
    markDictLoaded(prop)
  } catch (err) {
    ElMessage.error(`加载 ${item.label} 失败`)
    remoteOptions.value[prop] = []
  } finally {
    remoteLoading.value[prop] = false
  }
}

// ==================== 远程搜索（带关键词，防抖） ====================
let searchTimeout = null
function handleFilterChange(keyword, item) {
  const { prop, api, requestParams, keywordField = 'keyword', debounceWait = 300 } = item
  if (!api) return

  clearTimeout(searchTimeout)
  searchTimeout = setTimeout(async () => {
    try {
      remoteLoading.value[prop] = true

      // 构造请求体：requestParams + 分页 + 动态关键词字段
      const payload = {
        ...(typeof requestParams === 'function' ? requestParams() : requestParams || {}),
        [keywordField]: keyword // ← 动态字段名，如 name / companyName
      }

      const res = await request({
        url: api,
        method: 'post', // ← POST 请求
        data: payload // ← body 传参
      })

      const list =
        typeof item.transform === 'function'
          ? item.transform(res)
          : res.data?.records || res.data || []

      remoteOptions.value[prop] = list.map(i => ({
        value: i[item.valueKey || 'value'],
        label: i[item.labelKey || 'label'],
        raw: i // ← 保存完整对象
      }))
    } catch (err) {
      ElMessage.error(`搜索 ${item.label} 失败`)
    } finally {
      remoteLoading.value[prop] = false
    }
  }, debounceWait)
}

// ==================== 数字输入处理 ====================
// function handleNumberInput(value, item) {
//   const { inputType = 'text', decimalDigits = 2, prop } = item
//   if (!prop) return

//   let result = String(value ?? '').trim()

//   if (inputType === 'integer') {
//     // 只保留数字
//     result = result.replace(/[^\d]/g, '')
//   } else if (inputType === 'decimal') {
//     // 1. 只保留数字和小数点
//     result = result.replace(/[^\d.]/g, '')

//     // 2. 去掉开头的小数点（不允许 ".5" → 改为 "0.5" 更好，但这里先简单处理）
//     if (result.startsWith('.')) {
//       result = '0.' + result.slice(1)
//     }

//     // 3. 保证最多一个小数点
//     const parts = result.split('.')
//     if (parts.length > 2) {
//       result = parts[0] + '.' + parts.slice(1).join('')
//     }

//     // 4. 限制小数位数（但保留结尾的小数点！）
//     if (result.includes('.')) {
//       const [intPart, decPart] = result.split('.')
//       // 如果小数部分超过限制，截断
//       if (decPart.length > decimalDigits) {
//         result = intPart + '.' + decPart.slice(0, decimalDigits)
//       }
//       // ✅ 不再删除结尾的 '.'
//     }
//   }

//   // 防止重复赋值（可选优化）
//   if (localModel.value[prop] !== result) {
//     localModel.value = { ...localModel.value, [prop]: result }
//   }
// }
function handleNumberInput(value, item) {
  const { inputType = 'text', decimalDigits = 2, prop } = item
  if (!prop) return

  let result = String(value ?? '').trim()

  if (inputType === 'integer') {
    // 只保留数字和负号
    result = result.replace(/[^-\d]/g, '')

    // 如果有多个负号或者负号不在开头，则移除多余的负号
    if ((result.match(/-/g) || []).length > 1) {
      result = result.replace(/-/g, '').replace(/^/, '-') // 仅保留一个负号在最前面
    }
  } else if (inputType === 'decimalNumber') {
    // 可以输入正数，负数，小数
    // 1. 只保留数字、小数点和负号
    result = result.replace(/[^-\d.]/g, '')

    // 2. 处理负号：确保最多只有一个负号且必须在开头
    if ((result.match(/-/g) || []).length > 1) {
      result = result.replace(/-/g, '') // 移除所有负号
      if (result.startsWith('-')) {
        result = '-' + result.slice(1) // 确保负号在最前面
      } else {
        result = '-' + result // 如果原本没有负号但需要保留数值，可以省略此步骤
      }
    }

    // 3. 去掉开头的小数点（不允许 ".5" → 改为 "0.5" 更好）
    if (result.startsWith('.')) {
      result = '0' + result
    } else if (result.startsWith('-.')) {
      result = '-0' + result.slice(2)
    }

    // 4. 保证最多一个小数点
    const parts = result.split('.')
    if (parts.length > 2) {
      result = parts[0] + '.' + parts.slice(1).join('')
    }

    // 5. 限制小数位数（但保留结尾的小数点！）
    if (result.includes('.')) {
      const [intPart, decPart] = result.split('.')
      // 如果小数部分超过限制，截断
      if (decPart.length > decimalDigits) {
        result = intPart + '.' + decPart.slice(0, decimalDigits)
      }
      // ✅ 不再删除结尾的 '.'
    }
  } else if (inputType === 'decimal') {
    // 可以输入正整数和小数
    // 1. 只保留数字和小数点
    result = result.replace(/[^\d.]/g, '')

    // 2. 去掉开头的小数点（不允许 ".5" → 改为 "0.5" 更好，但这里先简单处理）
    if (result.startsWith('.')) {
      result = '0.' + result.slice(1)
    }

    // 3. 保证最多一个小数点
    const parts = result.split('.')
    if (parts.length > 2) {
      result = parts[0] + '.' + parts.slice(1).join('')
    }

    // 4. 限制小数位数（但保留结尾的小数点！）
    if (result.includes('.')) {
      const [intPart, decPart] = result.split('.')
      // 如果小数部分超过限制，截断
      if (decPart.length > decimalDigits) {
        result = intPart + '.' + decPart.slice(0, decimalDigits)
      }
      // ✅ 不再删除结尾的 '.'
    }
  }

  // 防止重复赋值（可选优化）
  if (localModel.value[prop] !== result) {
    localModel.value = { ...localModel.value, [prop]: result }
  }
}

// ==================== 暴露方法 ====================
defineExpose({
  getFormData() {
    return { ...localModel.value }
  },
  async validate() {
    return new Promise((resolve, reject) => {
      formRef.value?.validate(valid => {
        if (valid) resolve(localModel.value)
        else reject(new Error('Validation failed'))
      })
    })
  },
  resetForm() {
    const resetData = {}
    internalConfig.value.forEach(item => {
      const key = item.prop
      if (['checkbox-group', 'daterange', 'monthrange'].includes(item.type) || item.multiple) {
        resetData[key] = item.defaultValue ?? []
      } else if (item.type === 'upload') {
        resetData[key] = item.defaultValue ?? [] // upload 也是数组
      } else {
        resetData[key] = item.defaultValue ?? ''
      }
    })
    localModel.value = { ...resetData }
    nextTick(() => formRef.value?.clearValidate())
  },
  // ✅ 新增：强制刷新某个字段的远程选项
  async refreshRemoteOptions(targetProp) {
    console.log(`[SearchForm] 收到刷新请求: ${targetProp}`)

    // 1. 查找配置项
    const item = internalConfig.value.find(i => i.prop === targetProp)

    if (!item) {
      console.warn(`[SearchForm] 未找到 prop 为 ${targetProp} 的配置项`)
      return
    }

    if (item.type !== 'select' || !item.api) {
      console.warn(`[SearchForm] 字段 ${targetProp} 不是远程 Select 或没有 API`)
      return
    }

    console.log(`[SearchForm] 开始强制加载 ${targetProp} 的数据，API: ${item.api}`)

    // 2. 关键：在调用前，先清空旧数据，防止子组件内部的 "已加载则跳过" 逻辑生效
    // 如果你的 loadRemoteOptions 里有 "if (remoteOptions.value[prop]?.length > 0) return"
    // 这里必须先清空
    remoteOptions.value[targetProp] = []
    remoteLoading.value[targetProp] = true // 手动开启 loading

    try {
      // 3. 调用内部加载函数
      // 注意：直接调用 loadRemoteOptions，它会读取最新的 requestParams
      await loadRemoteOptions(item)
      console.log(`[SearchForm] ${targetProp} 加载完成`)
    } catch (error) {
      console.error(`[SearchForm] ${targetProp} 加载失败`, error)
      throw error
    } finally {
      remoteLoading.value[targetProp] = false
    }
  }
})
</script>

<style scoped>
/* 预览弹窗样式 */
.preview-container {
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 400px;
}

.preview-image-wrapper {
  text-align: center;
}

.preview-image {
  max-width: 100%;
  max-height: 70vh;
  object-fit: contain;
}

.preview-pdf {
  width: 100%;
  height: 70vh;
}

.preview-unsupported {
  text-align: center;
  padding: 40px;
}

.preview-unsupported p {
  margin: 16px 0;
  color: #909399;
}
.custom-upload-wrapper {
  width: 100%;
}

.custom-file-list {
  margin-top: 12px;
  border: 1px solid #dcdfe6;
  border-radius: 4px;
  background-color: #fff;
}

.file-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 8px 12px;
  border-bottom: 1px solid #ebeef5;
}

.file-item:last-child {
  border-bottom: none;
}

.file-name {
  flex: 1;
  font-size: 14px;
  color: #606266;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  margin-right: 16px;
}

.file-actions {
  display: flex;
  gap: 12px;
}
.formBox {
  box-sizing: border-box;
}

.search-form-item {
  margin-bottom: 20px;
}

.iconStyle {
  color: #409eff;
}
</style>
