<script setup lang="ts">
import type { FormInstance } from 'vant'
import { formatDate } from '@af-mobile-client-vue3/hooks/useCommon'
import { getConfigByName, getConfigByNameAsync, runLogic } from '@af-mobile-client-vue3/services/api/common'
import useSafecheckStore from '@af-mobile-client-vue3/stores/modules/safecheckStore'
import { useUserStore } from '@af-mobile-client-vue3/stores/modules/user'
import { setAppBackIntercept } from '@af-mobile-client-vue3/utils/appBackManager'
import { detectEnvironment } from '@af-mobile-client-vue3/utils/environment'
import { mobileUtil } from '@af-mobile-client-vue3/utils/mobileUtil'
import { getPhotosFromAny } from '@af-mobile-client-vue3/utils/PhotoClean'
import { executeStrFunctionByContext } from '@af-mobile-client-vue3/utils/runEvalFunction'
import { goBack } from '@af-mobile-client-vue3/utils/safeBack'
import { formatNow } from '@af-mobile-client-vue3/utils/timeUtil'
import hiddenDangers from '@af-mobile-client-vue3/views/SafeInspection/SecurityCertificate/OverallHiddenDangers/index.vue'
import photoAiRecognition from '@af-mobile-client-vue3/views/SafeInspection/SecurityCertificate/photoAiRecognition/index.vue'
import photoSignature from '@af-mobile-client-vue3/views/SafeInspection/SecurityCertificate/photoSignature/index.vue'
import QinHuaSignature from '@af-mobile-client-vue3/views/SafeInspection/SecurityCertificate/photoSignature/slots/QinHuaSignature.vue'
import GasDevice from '@af-mobile-client-vue3/views/SafeInspection/SecurityCertificate/slots/GasDevice.vue'
import userInfo from '@af-mobile-client-vue3/views/SafeInspection/SecurityCertificate/userInfo/index.vue'
import SecurityFormItem from '@af-mobile-client-vue3/views/SafeInspection/SecurityFormItem/index.vue'
import dayjs from 'dayjs/esm/index'
import moment from 'moment'
import {
  FloatingBubble,
  showConfirmDialog,
  showFailToast,
  showSuccessToast,
  showToast,
  Button as VanButton,
  CellGroup as VanCellGroup,
  Form as VanForm,
  Icon as VanIcon,
  Loading as VanLoading,
  Popover as VanPopover,
  Tab as VanTab,
  Tabs as VanTabs,
} from 'vant'
import {
  computed,
  getCurrentInstance,
  nextTick,
  onBeforeMount,
  onBeforeUnmount,
  onDeactivated,
  provide,
  reactive,
  ref,
  watch,
} from 'vue'
import { onBeforeRouteLeave, useRoute, useRouter } from 'vue-router'

defineOptions({ name: 'SecurityCertificate' })
// 获取路由实例
const router = useRouter()
const route = useRoute()

// 组件映射
const slotComponentMap = {
  gasDevice: GasDevice,
  photoAiRecognition,
}

// 拍照签名组件映射
const photoSignatureComponentMap = {
  photoSignature,
  qinHuaSignature: QinHuaSignature,
}
// 水印配置
const blurThreshold = ref<number>(30)
// 操作人信息
const checkerInfo = useUserStore().getUserInfo()
const safecheckStore = useSafecheckStore()
// 浮动气泡
const offset = ref({ x: 310, y: 500 })
const offset2 = ref({ x: 310, y: 110 })
const floatingBubbleStatus = ref(false)

// 安检计划id
const planId = ref(null)
// 计划项id
const checkId = ref(null)
// 入户时间
const onSiteDate = ref(null)
// 安检单模板id
const safeCheckTypeId = ref(null)
// 用户id
const userinfoId = ref(null)
// 安检单版本号
const safeCheckVersion = ref(null)
// 共同id （用于上传图片/安检单/录音时，关联到同一个文件）
const commonId = ref(null)
// 最新的一条录音数据
const latestRecordData = ref(null)
// 安检单id
const safeCheckPaperId = ref(null)
// 临时保存的数据类型
const draftType = ref(null)
// 调用时传入的额外数据 会存储在paper表的f_data字段中
const extData = ref(null)

// 派单部门列表
const dispatchDeptList = ref(null)
// 维修员列表
const repairmanList = ref(null)

// 用户信息组件引用
const userInfoRef = ref()

// 计算属性，从config.data中获取itemname作为下拉菜单的选项，并添加错误计数
const actions = ref([
  { text: '用户信息', value: 0, icon: '', errorCount: 0 },
])
const config = reactive({
  data: [],
})
const configReadonlyItems = reactive({
  data: [],
})

// 是否使用 临时数据初始化数据
const isDraft = ref('false')

const checkItemForm = ref<any[]>([])

const defectList = ref([])

// 悬浮气泡菜单项
const floatingBubbleActions = ref([])
// 小工具路由白名单（进入这些页面不拦截、不停止录音）
const toolRouteWhitelist = ref<Set<string>>(new Set())
// 缓存离开时的目标路由，供 onDeactivated 判断是否进入小工具
const leavingTarget = ref<{ path: string, name: string } | null>(null)
// 悬浮气泡外部点击定时器
const bubbleClickTimer = ref<number | null>(null)

const userDetail = ref(null)
const safeConfig = ref<any>(null)
const signatureFormData = ref<any>(null) // 存储签名表单数据
const signaturePhotoData = ref<any>([]) // 存储签名图片数据
const customDefectsData = ref<any[]>([]) // 存储自定义隐患数据
const isReadOnly = ref(false) // 是否为只读模式

const status = ref(false)
const showConfig = ref(true)

// 修改为使用对象管理多个表单引用
const formRefs = ref<FormInstance[]>([])
// 当前选中的tab索引
const activeTabIndex = ref(0)
// 子组件实例
const hiddenDangersRef = ref(null)
const photoSignatureRef = ref(null)
// slot组件引用
const slotRefs = ref<any[]>([])
// 录音状态
const isRecording = ref(false)
// 录音文件地址, 只保存录音文件名
const recordFile = ref([])
// 录音开关状态（从safeConfig中获取）
const recordSwitch = ref(false)
// 是否正在保存并准备返回，用于跳过路由离开拦截
const isSavingAndReturning = ref(false)

// 插槽组件初始数据, todo. 只有一个 插槽tab
const componentInitData = ref(null)

// 添加错误计数追踪
const errorCounts = reactive({
  userInfo: 0,
  tabs: [] as number[],
  hiddenDangers: 0,
  photoSignature: 0,
})

// 照片质量统计
const photoStats = reactive({
  rephotoCount: 0, // 翻拍照片数
  qualityFailedCount: 0, // 质量不合格照片数
})

function onPhotoQualityChange(type: string) {
  if (type === 'quality-failed') {
    console.warn('质量不合格照片数++')
    photoStats.qualityFailedCount++
  }
  else if (type === 'rephoto-detected') {
    console.warn('翻拍照片数++')
    photoStats.rephotoCount++
  }
}

// 计时器
const timer = ref(null)
const elapsedSeconds = ref(0)
const timerDisplay = ref('00:00')

const signatureAllInfo = ref(null)
// 是否显示保存按钮
const hideSaveButton = ref(false)

// redis中验证码的键
const verificationCodeKey = ref(`${checkerInfo.id}-${new Date().getTime()}-${Math.random().toString(36).slice(2, 5)}`)

/**
 * AI识别照片同步后记录对应属性及tab信息（itemId -> SyncedFieldInfo[]）
 * 用于：
 * 1. PhotoRecognitionInfo中标记已同步属性为蓝色可点击
 * 2. handleAiRecognitionJump中跳转到对应tab的指定表单项
 */
interface SyncedFieldInfo {
  rawKey: string // 原始属性key（如'位置'、'状态'），匹配AI识别结果的属性
  tabIndex: number // config.data中的tab索引，用于切换到正确的表单tab
  fieldName: string // 匹配到的表单项名称（res.itemname），用于定位具体VanField
}
const syncedItemInfoMap = ref<Record<string, SyncedFieldInfo[]>>({})

// 启动计时器
function startTimer() {
  elapsedSeconds.value = 0
  timerDisplay.value = '00:00'
  timer.value = setInterval(() => {
    elapsedSeconds.value++
    const minutes = Math.floor(elapsedSeconds.value / 60)
    const seconds = elapsedSeconds.value % 60
    timerDisplay.value = `${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`
  }, 1000)
}

// 停止计时器
function stopTimer() {
  if (timer.value) {
    clearInterval(timer.value)
    timer.value = null
  }
}

// 统一的录音控制 - 改造为Promise-callback函数
function stopRecordingSafely() {
  return new Promise((resolve, reject) => {
    try {
      mobileUtil.execute({
        funcName: 'stopRecording',
        param: { scene: 'security_check', planId: planId.value, checkId: checkId.value, userinfoId: userinfoId.value },
        callbackFunc: (res: any) => {
          console.log('停止录音===', res)
          if (res && res.status === 'success') {
            isRecording.value = false
            resolve(res)
          }
          else {
            isRecording.value = false
            reject(new Error(res?.message || '停止录音失败'))
          }
        },
      })
    }
    catch (error) {
      isRecording.value = false
      reject(error)
    }
  })
}

function startRecordingSafely() {
  // 检查是否应该开启录音：非只读模式且配置中录音开关开启
  console.log('isReadOnly.value', isReadOnly.value)
  console.log('recordSwitch.value', recordSwitch.value)
  if (isReadOnly.value || !recordSwitch.value) {
    console.log('录音功能已关闭，跳过录音启动 - 只读模式:', isReadOnly.value, '录音开关:', recordSwitch.value)
    return
  }

  // 开始前先尝试停止一次，避免上一次未释放导致初始化失败
  // stopRecordingSafely()
  try {
    mobileUtil.execute({
      funcName: 'startRecording',
      param: {
        scene: 'security_check',
        planId: planId.value,
        checkId: checkId.value,
        userinfoId: userinfoId.value,
      },
      callbackFunc: (result: any) => {
        showSuccessToast(JSON.stringify(result))
        if (result.status === 'success') {
          latestRecordData.value = result.data.filePath
          recordFile.value.push(result.data.filePath.split('/').pop())
          isRecording.value = true
          showSuccessToast(`已自动开启录音！`)
        }
        else {
          isRecording.value = false
          showFailToast(`打开录音失败，${result.message ?? '请检查录音权限是否开启！'}`)
          // App 环境下直接返回
          if (detectEnvironment()?.isApp) {
            goBack()
          }
        }
      },
    })
    isRecording.value = true
  }
  catch {}
}

// 初始化变更函数
function runInitCustomFunctions() {
  const groups: any[] = (config as any).data
  if (!Array.isArray(groups))
    return
  groups.forEach((group: any) => {
    const items = group?.items?.value || group?.items
    if (!Array.isArray(items))
      return
    items.forEach((item: any) => {
      const redundant = item?.f_data
      if (typeof redundant !== 'string' || redundant.trim() === '')
        return
      try {
        const extra: any = JSON.parse(redundant)
        const funcStr = extra && typeof extra.f_custom_function === 'string' ? extra.f_custom_function.trim() : ''
        if (!funcStr)
          return
        executeStrFunctionByContext(getCurrentInstance(), funcStr, [item, config as any, userDetail.value])
      }
      catch (err) {
        console.warn('执行自定义函数错误：', err)
      }
    })
  })
}

// 初始化SafeCheckConfig配置
function initSafeCheckConfig(enableFieldMapping = true) {
  // 处理字段映射配置（可通过参数控制是否执行）
  if (enableFieldMapping && safeConfig.value && safeConfig.value.fieldMappingItems && safeConfig.value.fieldMapping) {
    // 遍历config.data中的配置项
    config.data.forEach((configItem) => {
      // 检查itemname是否存在于fieldMappingItems中
      if (safeConfig.value.fieldMappingItems.includes(configItem.itemname)) {
        // 检查是否已存在相同的 tab，避免重复添加
        const existingTab = checkItemForm.value.find(
          (tab: any) => tab.tabName === configItem.itemname && tab.type === 'normal',
        )

        if (existingTab) {
          // 如果已存在，合并数据而不是清空（避免覆盖已恢复的草稿数据）
          const itemOne = existingTab
          // 确保 items 是数组
          if (!Array.isArray(itemOne.items)) {
            itemOne.items = []
          }

          // 如果已有数据，说明是从草稿恢复的，只更新 readonly 属性，不更新值
          const hasExistingData = itemOne.items.length > 0

          // 遍历该项的items
          if (configItem.items && Array.isArray(configItem.items)) {
            configItem.items.forEach((item) => {
              // 查找匹配的字段映射
              const fieldMapping = safeConfig.value.fieldMapping.find((mapping: any) =>
                mapping.formKey === item.itemname,
              )

              if (fieldMapping) {
                // 如果有readonly属性，也进行赋值（这个不影响数据）
                if (fieldMapping.readonly !== undefined) {
                  item.readonly = fieldMapping.readonly
                }

                // 如果已有数据（从草稿恢复），不更新值，只更新 readonly 属性
                if (hasExistingData) {
                  // 只更新 config.data 中的 readonly 属性，不更新值
                  return
                }

                // 如果没有数据（新建时），正常添加字段映射的值
                const sourceValue = userDetail.value?.userinfo?.[fieldMapping.sourceKey]

                if (sourceValue !== undefined) {
                  // 检查是否已存在相同的 itemName
                  const existingItemIndex = itemOne.items.findIndex((i: any) => i.itemName === item.itemname)

                  if (existingItemIndex < 0) {
                    // 如果不存在，添加新项
                    const hasExisting = !(item.value === undefined || item.value === null || String(item.value) === '')
                    const finalValue = hasExisting ? item.value : sourceValue
                    if (!hasExisting)
                      item.value = sourceValue
                    itemOne.items.push({
                      itemName: item.itemname,
                      value: finalValue,
                    })
                  }
                }
              }
            })
          }
        }
        else {
          // 如果不存在，创建新的 tab
          const itemOne = {
            tabName: configItem.itemname,
            type: 'normal',
            items: [],
          }
          // 遍历该项的items
          if (configItem.items && Array.isArray(configItem.items)) {
            configItem.items.forEach((item) => {
              // 查找匹配的字段映射
              const fieldMapping = safeConfig.value.fieldMapping.find((mapping: any) =>
                mapping.formKey === item.itemname,
              )

              if (fieldMapping) {
                // 从userDetail.value.userinfo中获取sourceKey对应的值
                const sourceValue = userDetail.value?.userinfo?.[fieldMapping.sourceKey]

                if (sourceValue !== undefined) {
                  // 仅当当前没有值时才用映射默认值，避免覆盖缓存/用户填写的值
                  const hasExisting = !(item.value === undefined || item.value === null || String(item.value) === '')
                  const finalValue = hasExisting ? item.value : sourceValue
                  if (!hasExisting)
                    item.value = sourceValue
                  itemOne.items.push({
                    itemName: item.itemname,
                    value: finalValue,
                  })
                }
                // 如果有readonly属性，也进行赋值
                if (fieldMapping.readonly !== undefined) {
                  item.readonly = fieldMapping.readonly
                }
              }
            })
          }
          checkItemForm.value.push(itemOne)
        }
      }
    })
  }
}
// 处理表单项默认值与上传配置
function processFormItemDefaults() {
  config.data.forEach((group) => {
    const items = group.items?.value || group.items
    if (Array.isArray(items)) {
      items.forEach((item) => {
        // radio / selector 默认值
        if (item.type === 'radio' || item.type === 'selector') {
          if (Array.isArray(item.options)) {
            const def = item.options.find(opt => opt.isdefault === true)
            if (def)
              item.value = def.data
          }
        }
        // picture / files 的 photoNumber 与 isLoad
        if (item.type === 'picture' || item.type === 'files') {
          if (item.redundantData) {
            try {
              const extra = JSON.parse(item.redundantData)
              item.photoNumber = extra?.f_number ? Number(extra.f_number) : 3
              item.isLoad = extra?.f_upload ? (extra.f_upload === '是') : false
            }
            catch {
              item.photoNumber = 3
              item.isLoad = false
            }
          }
          else {
            item.photoNumber = 3
            item.isLoad = false
          }
        }
      })
    }
  })
}

/**
 * ai识别数据和实际key对应map
 * AI标签 → 标准字段
 */
const aiKeyToFieldMap = ref({})

onBeforeMount(() => {
  planId.value = route.query.planId as string
  checkId.value = route.query.checkId as string
  safeCheckTypeId.value = route.query.safeCheckTypeId as string
  userinfoId.value = route.query.userinfoid as string
  isDraft.value = route.query.isDraft as string
  // 安检单id
  safeCheckPaperId.value = route.query.paperId as string
  // 临时保存类型
  draftType.value = route.query.draftType as string
  // 是否隐藏保存按钮
  hideSaveButton.value = route.query.hideSaveButton as string === 'true'
  // 将路由中的extData参数存储到安检单的f_data中
  try {
    const ext = JSON.parse(route.query.extData as string)
    if (ext !== null && typeof ext === 'object' && !Array.isArray(ext)) {
      extData.value = ext
    }
  }
  catch (e) {
    console.log('extData解析失败！')
    // 非对象则不进行赋值
  }
  // 判断是否为只读模式：当存在 paperId 且 (mode 未传或 mode !== '1') 时为只读，否则为可编辑
  const hasPaperId = !!route.query.paperId
  const mode = route.query.mode as string | undefined
  isReadOnly.value = !!(hasPaperId && (!mode || mode !== '1'))
  console.log('当前只读状态:', isReadOnly.value, 'paperId:', route.query.paperId, 'mode:', route.query.mode)

  // 统一转交到 continuePageInit 决定是否弹窗
  continuePageInit()
  getConfigByName('PhotoWatermarkConfig', (result) => {
    if (result) {
      blurThreshold.value = result?.blurThreshold || 30
    }
  }, 'af-safecheck')
  // 获取对应aiKeyToFieldMap
  getConfigByName('AICheckConfig', (res: any) => {
    aiKeyToFieldMap.value = res?.aiKeyToFieldMap || {
      燃气表被包裹: '燃气管道及表具包裹',
      管道被包裹: '燃气管道及表具包裹',
      气表: '气表照片',
    }
  }, 'af-safecheck')
})

function initParseData(res) {
  // 清空 checkItemForm，避免恢复草稿时数据重复
  checkItemForm.value = []

  // 提交logic 中, 获取的数据
  const templateJson = Array.isArray(res?.templateJson)
    ? res.templateJson
    : (res?.templateJson ? JSON.parse(res.templateJson) : [])
  if (res.checkPaper && res.checkPaper.length > 0) {
    safeCheckTypeId.value = res.checkPaper[0].f_safecheck_type_id
    safeCheckVersion.value = res.checkPaper[0].f_safe_type_version
    planId.value = res.checkPaper[0].f_plan_id
    checkId.value = res.checkPaper[0].f_plan_item_id
    userinfoId.value = res.checkPaper[0].f_userinfo_id
    onSiteDate.value = res.checkPaper[0].f_onsite_time
  }
  else {
    showFailToast('未查询到相应安检结果，请刷新后重试！')
    clear()
    // 提交成功后返回
    goBack()
  }
  config.data = templateJson
  processFormItemDefaults()
  const paper = Array.isArray(res?.checkPaper) ? res.checkPaper[0] : null

  // 提交数据中的 checkItemForm
  const safeContent = paper && typeof paper.f_safe_content === 'string'
    ? JSON.parse(paper.f_safe_content)
    : (paper?.f_safe_content || [])

  mergeSafeContentByTabAndType(templateJson || [], safeContent || [])

  // 基本信息页数据
  const userInfoTab = Array.isArray(safeContent) ? safeContent.find((t: any) => t?.type === 'userInfo') : null
  if (userInfoTab && Array.isArray(userInfoTab.items) && userInfoTab.items[0])
    userDetail.value = { userinfo: userInfoTab.items[0] }

  // 提交数据中 type === 'photoSignature', 添加了一个 formConfig key
  // 获取logic 中 -——>signatureFormConfig = liuLiConfig.get("lngSecurityChecktestForm")
  // 处理签名表单数据
  if (res.signatureForm) {
    const { formConfig, signaturePhoto, ...signatureFormWithoutConfig } = res.signatureForm
    signatureFormData.value = signatureFormWithoutConfig
    signaturePhotoData.value = signaturePhoto
    // 用于存储签名表单的所有信息，供给动态组件的后续使用
    signatureAllInfo.value = res.signatureForm
  }

  // 提交数据中 defectList, 注意key 的 转换
  // 处理隐患数据，筛选出自定义隐患
  if (res.defectList && Array.isArray(res.defectList)) {
    customDefectsData.value = res.defectList.filter((defect: any) =>
      defect.f_item_type === '自定义隐患',
    )
  }

  updateActions()

  // 初始化SafeCheckConfig配置（查看模式不需要字段映射）
  initSafeCheckConfig(true)
  // 初始化时执行一次自定义函数
  try {
    runInitCustomFunctions()
  }
  catch (e) {
    console.warn('初始化执行自定义函数失败：', e)
  }
  status.value = true
  init()
}

// 继续页面初始化的函数
async function continuePageInit() {
  getConfigByName('SafeCheckConfig', (configData: any) => {
    // 保存配置到组件状态
    safeConfig.value = configData
    // 从配置中获取录音开关状态
    recordSwitch.value = !!(safeConfig.value && safeConfig.value.recordSwitch === true)
    console.log('录音开关状态:', recordSwitch.value)

    // 获取派单部门列表
    runLogic('getScDepartment', { orgid: checkerInfo.orgid }, 'af-system').then((res: any) => {
      console.log('dispatchDeptList===', res)
      if (res) {
        dispatchDeptList.value = res
      }
    })
    // 获取维修员列表
    runLogic('getRepairUserByDepId', { deptId: checkerInfo.depids }, 'af-system').then((res: any) => {
      console.log('repairmanList===', res)
      if (res) {
        repairmanList.value = res
      }
    })

    if (route.query.paperId) {
      runLogic('getCheckResult', { paperId: route.query.paperId }, 'af-safecheck').then((res: any) => {
        try {
          // 保存共同id
          commonId.value = Date.now() + Math.random().toString(36).substr(2, 5)
          initParseData(res)
        }
        catch (e) {
          console.warn('解析或合并安检查看数据失败：', e)
        }
      })
    }
    else {
      // 无 paperId（新建）：先弹窗确认是否开始计时并开启录音
      if (!isReadOnly.value) {
        showConfirmDialog({
          title: '入户提示',
          message: '是否开始安检计时并开启录音？',
          confirmButtonText: '开始计时',
          cancelButtonText: '只读模式',
        }).then(async () => {
          // 确认：设置入户时间，保持编辑模式
          const dateRes: any = await runLogic('getNowDateTime', { num: 1 }, 'af-safecheck')
          onSiteDate.value = dateRes?.nowDateTime || formatDate(new Date())
          if (isDraft.value === 'true' && !route.query.paperId) {
            // 获取临时保存数据
            const res = await runLogic('getDraft', { id: checkId.value }, 'af-safecheck') as any
            if (res.msg) {
              try {
                // 保存共同id
                commonId.value = res.data?.commonId || ''
                console.log('草稿数据:-----', res.data)
                initParseData(res.data)
                recordFile.value = res.data?.recordFile || []
                startRecordingSafely()
                return
              }
              catch (e) {
                console.warn('解析或合并安检查看数据失败：', e)
              }
            }
          }
          console.log('用户选择开始安检计时，入户时间:', onSiteDate.value)
          // 继续新建流程
          runLogic('GetSecurityCheckById', { safeCheckTypeId: safeCheckTypeId.value, safeCheckType: route.query.safeCheckType, userType: route.query.userType }, 'af-safecheck').then((res: any) => {
            if (res && res.f_json) {
              // 保存共同id
              commonId.value = Date.now() + Math.random().toString(36).substr(2, 5)
              config.data = JSON.parse(res.f_json) // 原始数据
              safeCheckVersion.value = res.version
              safeCheckTypeId.value = res.id
              processFormItemDefaults()
              updateActions()
              runLogic('getPlanItemDetailById', { id: checkId.value, f_userinfo_id: userinfoId.value }, 'af-safecheck').then(async (res: any) => {
                userDetail.value = res

                if (res.devices && Array.isArray(res.devices) && res.devices.length > 0) {
                  const deviceConfig = config.data.find(item => item.type === 'slot' && item.slotName === 'gasDevice')
                  if (deviceConfig) {
                    deviceConfig.deviceData = res.devices.map((device: any) => ({
                      deviceId: device.deviceId || `device_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`,
                      f_devices_type: device.deviceType || device.f_devices_type,
                      deviceCode: device.deviceCode || device.code,
                      groupName: deviceConfig.itemname,
                      items: [],
                      ...device,
                    }))
                  }
                }
                initSafeCheckConfig(true)
                try {
                  runInitCustomFunctions()
                }
                catch (e) { console.warn('初始化执行自定义函数失败：', e) }
                status.value = true
                startRecordingSafely()
              })
              init()
            }
            else {
              showFailToast('未查询到相应安检单配置，请联系管理员！')
              clear()
              goBack()
            }
          })
        }).catch(() => {
          // 取消：进入只读模式
          isReadOnly.value = true
          console.log('用户选择只读模式进入')
          runLogic('GetSecurityCheckById', { safeCheckTypeId: safeCheckTypeId.value, safeCheckType: route.query.safeCheckType, userType: route.query.userType }, 'af-safecheck').then((res: any) => {
            if (res && res.f_json) {
              config.data = JSON.parse(res.f_json)
              safeCheckVersion.value = res.version
              safeCheckTypeId.value = res.id
              processFormItemDefaults()
              updateActions()
              runLogic('getPlanItemDetailById', { id: checkId.value, f_userinfo_id: userinfoId.value }, 'af-safecheck').then((res: any) => {
                userDetail.value = res
                initSafeCheckConfig(false)
                try {
                  runInitCustomFunctions()
                }
                catch (e) { console.warn('初始化执行自定义函数失败：', e) }
                status.value = true
                init()
              })
            }
            else {
              showFailToast('未查询到相应安检单配置，请联系管理员！')
              goBack()
              clear()
            }
          })
        })
      }
      else {
        // 已是只读模式，直接加载为查看
        runLogic('GetSecurityCheckById', { safeCheckTypeId: safeCheckTypeId.value, safeCheckType: route.query.safeCheckType, userType: route.query.userType }, 'af-safecheck').then((res: any) => {
          if (res && res.f_json) {
            config.data = JSON.parse(res.f_json)
            safeCheckVersion.value = res.version
            safeCheckTypeId.value = res.id
            processFormItemDefaults()
            updateActions()
            runLogic('getPlanItemDetailById', { id: checkId.value, f_userinfo_id: userinfoId.value }, 'af-safecheck').then((res: any) => {
              userDetail.value = res
              initSafeCheckConfig(false)
              try {
                runInitCustomFunctions()
              }
              catch (e) { console.warn('初始化执行自定义函数失败：', e) }
              status.value = true
              init()
            })
            init()
          }
          else {
            showFailToast('未查询到相应安检单配置，请联系管理员！')
            goBack()
            clear()
          }
        })
      }
    }
    // 启动计时器
    if (safeConfig.value?.isTimer && !isReadOnly.value) {
      startTimer()
    }
  }, 'af-safecheck')
}
/**
 * 过滤 config 中所有 items，移除 readonly === true 的字段
 * @param {Array} configData 原始配置
 * @returns {Array} 新配置（已过滤）
 */
function filterReadonlyItems(configData: Array<any>): Array<any> {
  if (!Array.isArray(configData))
    return []

  return configData.map((slot) => {
    const items = Array.isArray(slot.items) ? slot.items : []

    return {
      ...slot,
      items: items.filter((item: any) => item?.readonly !== true),
    }
  })
}
function init() {
  // 只读模式下直接移除 photoAiRecognition
  if (isReadOnly.value) {
    config.data = config.data.filter(item => item.slotName !== 'photoAiRecognition')
  }
  configReadonlyItems.data = filterReadonlyItems(config.data)
  getConfigByName('CheckSmallToolConfig', (result) => {
    const actions = [
      ...result.toolList,
    ]
    // 仅当存在 计划安检/临时安检-不是只读 时，提供"清理临时数据"功能
    if (!safeCheckPaperId.value && !isReadOnly.value) {
      actions.push({ title: '清空保存数据', icon: 'bulb-o', routerName: '清空保存数据' })
    }
    // 返回首页始终存在
    actions.push({ title: '返回上级', icon: 'home-o', routerName: '返回上级' })

    floatingBubbleActions.value = actions
    try {
      const names: string[] = Array.isArray(result.toolList)
        ? result.toolList.map((t: any) => t?.routerName).filter((s: any) => typeof s === 'string' && s)
        : []
      toolRouteWhitelist.value = new Set(names)
    }
    catch {}
  }, 'af-safecheck')
}

// 立即清理本地界面状态（不依赖后端返回）
function resetLocalState() {
  // 重置基础数据
  config.data = []
  configReadonlyItems.data = []
  checkItemForm.value = []
  defectList.value = []
  customDefectsData.value = []
  signatureFormData.value = null
  signaturePhotoData.value = []
  componentInitData.value = null
  // 派单部门列表
  dispatchDeptList.value = null
  // 维修员列表
  repairmanList.value = null

  // 清空引用与错误计数
  formRefs.value = []
  slotRefs.value = []
  errorCounts.userInfo = 0
  errorCounts.tabs = []
  errorCounts.hiddenDangers = 0
  errorCounts.photoSignature = 0
  photoStats.rephotoCount = 0
  photoStats.qualityFailedCount = 0

  // 重置导航
  actions.value = [{ text: '用户信息', value: 0, icon: '', errorCount: 0 }]
  activeTabIndex.value = 0

  // 清空用户信息与配置
  userDetail.value = null
  safeConfig.value = null

  // 显示加载中占位
  status.value = false
}

// 重新加载全新的表单（跳过弹窗，进入编辑模式）
async function reloadNewForm() {
  // 保持编辑模式
  isReadOnly.value = false
  try {
    const res: any = await runLogic('GetSecurityCheckById', {
      safeCheckTypeId: safeCheckTypeId.value,
      safeCheckType: route.query.safeCheckType,
      userType: route.query.userType,
    }, 'af-safecheck')
    if (res && res.f_json) {
      // 重新加载时，生成新的 commonId
      commonId.value = Date.now() + Math.random().toString(36).substr(2, 5)
      config.data = JSON.parse(res.f_json)
      safeCheckVersion.value = res.version
      safeCheckTypeId.value = res.id
      processFormItemDefaults()
      updateActions()

      userDetail.value = await runLogic('getPlanItemDetailById', {
        id: checkId.value,
        f_userinfo_id: userinfoId.value,
      }, 'af-safecheck')
      if (userDetail.value.devices && Array.isArray(userDetail.value.devices) && userDetail.value.devices.length > 0) {
        const deviceConfig = config.data.find(item => item.type === 'slot' && item.slotName === 'gasDevice')
        if (deviceConfig) {
          deviceConfig.deviceData = userDetail.value.devices.map((device: any) => ({
            deviceId: device.deviceId || `device_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`,
            f_devices_type: device.deviceType || device.f_devices_type,
            deviceCode: device.deviceCode || device.code,
            groupName: deviceConfig.itemname,
            items: [],
            ...device,
          }))
        }
      }
      safeConfig.value = await getConfigByNameAsync('SafeCheckConfig', 'af-safecheck')

      // 初始化 SafeCheckConfig 与自定义函数
      initSafeCheckConfig(true)
      try {
        runInitCustomFunctions()
      }
      catch (e) {
        console.warn('初始化执行自定义函数失败：', e)
      }

      status.value = true
      activeTabIndex.value = 0
    }
    else {
      showFailToast('未查询到相应安检单配置，请联系管理员！')
      goBack()
      clear()
    }
  }
  catch (e) {
    console.warn('重新加载表单失败：', e)
    showFailToast('刷新失败，请稍后重试')
  }
}

// 确保所有标签页被初始化的函数
async function initAllTabs() {
  const currentTab = activeTabIndex.value

  // 遍历激活每个标签页
  for (let i = 0; i < actions.value.length; i++) {
    activeTabIndex.value = i
    await nextTick() // nextTick确保DOM完全更新
  }

  // 恢复原来的标签页
  activeTabIndex.value = currentTab
  await nextTick()
}

// 同步整体隐患子页的隐患到父级 defectList（仅替换 tabName 为"整体隐患"且 type 为"CustomHiddenDanger"的部分）
function onHiddenDangersUpdate(newDefects: any[]) {
  // 保留其他来源的隐患（安检项、设备隐患等）
  const others = (defectList.value || []).filter(d =>
    !(d?.tabName === '自定义隐患' && d?.type === 'CustomHiddenDanger'),
  )

  // 安全处理新隐患数据
  const safeNew = Array.isArray(newDefects) ? newDefects : []

  // 合并隐患列表
  defectList.value = [...others, ...safeNew]
  console.log('最新隐患列表===', defectList.value)
}

// 清理已删除设备的数据（根据 deviceId）
function cleanupDeletedDeviceData(deviceId: string, groupName: string) {
  if (!deviceId)
    return
  // 清理变更冗余（device tab 的分组）
  const dTab = (checkItemForm.value || []).find((t: any) => (t.type || 'normal') === 'device' && t.tabName === groupName)
  if (dTab && Array.isArray(dTab.items)) {
    dTab.items = dTab.items.filter((i: any) => i.deviceId !== deviceId)
    // 如果设备检查 tab 变为空，移除该 tab
    if (dTab.items.length === 0) {
      removeTabIfEmpty(groupName, 'device')
    }
  }
  // 清理该设备的隐患
  defectList.value = (defectList.value || []).filter((d: any) => d.deviceId !== deviceId)
}

// 当状态变为true时（数据加载完成后）初始化所有标签页
const stopStatusWatch = watch(() => status.value, async (newVal) => {
  if (newVal) {
    await nextTick()
    await initAllTabs()
    // 初始化错误计数数组
    errorCounts.tabs = Array.from({ length: config.data.length }).fill(0) as number[]
  }
})

// 验证所有表单并更新错误计数
async function validateAllForms() {
  // 重置所有错误计数
  errorCounts.userInfo = 0
  errorCounts.tabs.fill(0)
  errorCounts.hiddenDangers = 0
  errorCounts.photoSignature = 0

  // 存储错误信息，以便在提示时使用
  let firstErrorMessage = ''

  // 再次确保所有标签页已初始化
  await initAllTabs()

  // 验证用户信息表单
  if (userDetail.value && userInfoRef.value) {
    try {
      await userInfoRef.value.validate()
    }
    catch (error) {
      // 更新错误计数
      errorCounts.userInfo = error.length || 0
      // 记录第一条错误信息
      if (!firstErrorMessage && error && error[0])
        firstErrorMessage = error[0]
    }
  }
  // 验证常规表单和slot组件
  for (let i = 0; i < config.data.length; i++) {
    const configItem = config.data[i]

    if (configItem.type === 'slot') {
      // 验证slot组件
      const slotInstance = slotRefs.value[i]
      if (slotInstance && typeof slotInstance.validate === 'function') {
        try {
          await slotInstance.validate()
        }
        catch (error) {
          // 更新错误计数
          errorCounts.tabs[i] = error.length || 0
          // 记录第一条错误信息
          if (!firstErrorMessage && error && error[0])
            firstErrorMessage = error[0]
        }
      }
    }
    else {
      // 验证常规表单
      const formInstance = formRefs.value[i]
      if (formInstance) {
        try {
          await formInstance.validate()
        }
        catch (error) {
          // 更新错误计数，使用实际的表单索引
          errorCounts.tabs[i] = error.length || 0
          // 记录第一条错误信息
          if (!firstErrorMessage && error && error[0])
            firstErrorMessage = error[0]
        }
      }
    }
  }
  // 验证整体隐患组件
  if (hiddenDangersRef.value) {
    try {
      await hiddenDangersRef.value.validate()
    }
    catch (error) {
      errorCounts.hiddenDangers = error.length || 0
      // 记录第一条错误信息
      if (!firstErrorMessage && error && error[0])
        firstErrorMessage = error[0]
    }
  }
  // 验证拍照签名组件
  if (photoSignatureRef.value) {
    try {
      await photoSignatureRef.value.validate()
    }
    catch (error) {
      errorCounts.photoSignature = error.length || 0
      // 如果还没有记录错误信息，记录拍照签名的第一条错误
      if (!firstErrorMessage && error && error[0])
        firstErrorMessage = error[0]
    }
  }

  return {
    hasErrors: errorCounts.userInfo > 0 || errorCounts.tabs.some(count => count > 0) || errorCounts.photoSignature > 0 || errorCounts.hiddenDangers > 0,
    firstErrorMessage,
  }
}

// 组织提交数据
async function organizeSubmitData() {
  // 获取用户信息组件数据
  const userInfoData = userInfoRef.value?.getData()
  // 获取用气设备组件数据
  let gasDeviceData = null
  if (slotRefs.value && slotRefs.value.length > 0) {
    // 找到 gasDevice 插槽组件
    const gasDeviceIndex = config.data.findIndex(item => item.slotName === 'gasDevice')
    if (gasDeviceIndex >= 0 && slotRefs.value[gasDeviceIndex]) {
      const gasDeviceRef = slotRefs.value[gasDeviceIndex]
      if (gasDeviceRef && typeof gasDeviceRef.getFormData === 'function') {
        gasDeviceData = gasDeviceRef.getFormData()
        // 清洗数据：提取每条数据中除 deviceItems 之外的全部属性
        if (Array.isArray(gasDeviceData)) {
          gasDeviceData = gasDeviceData.map((device) => {
            // const { deviceItems, ...cleanedDevice } = device
            return device
          })
        }
      }
    }
  }
  // 将 gasDeviceData 中的设备信息作为 form 属性添加到设备检查项中
  if (gasDeviceData && Array.isArray(gasDeviceData)) {
    // 从安检项配置中找到类型为设备的 itemname
    const deviceConfigItem = config.data.find(item => item.type === 'slot' && item.slotName === 'gasDevice')
    const deviceTabName = deviceConfigItem?.itemname

    // 找到设备检查 tab，如果不存在则创建
    let deviceCheckTab = checkItemForm.value.find(tab => tab.type === 'device' && tab.tabName === deviceTabName)

    if (!deviceCheckTab) {
      // 如果设备检查 tab 不存在，创建一个
      deviceCheckTab = {
        tabName: deviceTabName,
        type: 'device',
        items: [],
      }
      checkItemForm.value.push(deviceCheckTab)
    }

    // 确保 deviceCheckTab.items 是数组
    if (!Array.isArray(deviceCheckTab.items)) {
      deviceCheckTab.items = []
    }

    // 遍历 gasDeviceData 中的每个设备
    gasDeviceData.forEach((gasDevice) => {
      // 在设备检查 tab 中查找是否已存在该设备
      let existingDeviceGroup = deviceCheckTab.items.find(item => item.deviceId === gasDevice.deviceId)

      if (!existingDeviceGroup) {
        // 如果设备分组不存在，创建一个新的设备分组
        existingDeviceGroup = {
          deviceId: gasDevice.deviceId,
          deviceType: gasDevice.f_devices_type,
          groupName: deviceCheckTab.tabName,
          items: [], // 安检项为空数组
          form: gasDevice, // 将设备信息作为 form 属性存储
        }
        deviceCheckTab.items.push(existingDeviceGroup)
      }
      else {
        // 如果设备分组已存在，更新 form 属性
        existingDeviceGroup.form = gasDevice
      }
    })
  }
  // 获取拍照签名组件数据
  const photoSignatureData = photoSignatureRef.value.getFormData()
  const signaturePhoto = photoSignatureRef.value.getSignatureList()

  // 将拍照签名中的媒体字段从对象数组还原为字符串数组（用于提交）
  const toIdArray = (arr: any) => getPhotosFromAny({ v: arr }, 'v')
  if (photoSignatureData && typeof photoSignatureData === 'object') {
    if (Array.isArray((photoSignatureData as any).Images))
      (photoSignatureData as any).Images = toIdArray((photoSignatureData as any).Images)
    if (Array.isArray((photoSignatureData as any).FilesId))
      (photoSignatureData as any).FilesId = toIdArray((photoSignatureData as any).FilesId)
  }
  const signaturePhotoIds = toIdArray(signaturePhoto)
  // 构建完整的checkItemForm，并进行去重处理
  const rawCheckItemForm: any[] = Array.isArray(checkItemForm.value) ? [...checkItemForm.value] : []

  // 去重：按 tabName 和 type 去重，保留最后一个
  const tabMap = new Map<string, any>()
  rawCheckItemForm.forEach((tab: any) => {
    if (!tab || typeof tab !== 'object')
      return
    const key = `${tab.tabName || ''}_${tab.type || 'normal'}`
    tabMap.set(key, tab)
  })
  const deduplicatedCheckItemForm = Array.from(tabMap.values())

  // 添加用户信息和拍照签名
  deduplicatedCheckItemForm.unshift({ tabName: '用户信息', type: 'userInfo', items: [userInfoData] })
  const photoData: any = { tabName: '拍照签名', type: 'photoSignature', items: [photoSignatureData], f_signature_photo: signaturePhotoIds }
  if (recordFile.value && recordFile.value.length) {
    photoData.recordFile = recordFile.value
  }
  deduplicatedCheckItemForm.push(photoData)

  // 获取photoAiRecognition组件数据
  if (slotRefs.value && slotRefs.value.length > 0) {
    let photoAiData = null
    // 找到 photoAiRecognition 插槽组件
    const photoAiIndex = config.data.findIndex(item => item.slotName === 'photoAiRecognition')
    if (photoAiIndex >= 0 && slotRefs.value[photoAiIndex]) {
      photoAiData = slotRefs.value[photoAiIndex].getHiddenDanger()
      deduplicatedCheckItemForm.push({
        tabName: 'AI拍照识别',
        type: 'photoAiRecognition',
        items: [photoAiData],
      })
    }
  }
  // 使用去重后的数组
  const completeCheckItemForm = deduplicatedCheckItemForm

  // 统一将所有项中的媒体字段（对象数组）转换为字符串数组
  const toIds = (arr: any) => getPhotosFromAny({ v: arr }, 'v')
  const normalizeItemMedia = (rec: any) => {
    if (!rec || typeof rec !== 'object')
      return rec
    if (Array.isArray(rec.value))
      rec.value = toIds(rec.value)
    if (Array.isArray(rec.f_defect_paths))
      rec.f_defect_paths = toIds(rec.f_defect_paths)
    if (Array.isArray(rec.f_process_paths))
      rec.f_process_paths = toIds(rec.f_process_paths)
    return rec
  }

  completeCheckItemForm.forEach((tab: any) => {
    if (!Array.isArray(tab?.items))
      return
    // 设备类型 items 里仍然是记录数组，按相同规则处理
    tab.items = tab.items.map((rec: any) => normalizeItemMedia(rec))
  })

  // 收集隐藏项的函数
  const collectHiddenItems = () => {
    completeCheckItemForm.forEach((tab: any) => {
      if (!tab || typeof tab !== 'object')
        return

      const tabName = tab.tabName
      const tabType = tab.type || 'normal'
      const hiddenItems: string[] = []

      try {
        // 只处理 normal 和 device 类型
        if (tabType === 'normal') {
          // 普通分组：从 config.data 中找到对应的分组
          const configGroup = config.data.find((grp: any) => grp?.itemname === tabName)
          if (configGroup) {
            const items = configGroup?.items?.value || configGroup?.items
            if (Array.isArray(items)) {
              items.forEach((it: any) => {
                if (it && it.show === false && it.itemname) {
                  hiddenItems.push(it.itemname)
                }
              })
            }
          }
        }
        else if (tabType === 'device') {
          // 设备分组：需要为每个设备单独收集隐藏项
          if (Array.isArray(tab.items)) {
            // 从 config.data 中找到 gasDevice slot 配置，获取源设备检查项
            const deviceConfig = config.data.find((grp: any) => grp?.type === 'slot' && grp?.slotName === 'gasDevice')
            if (deviceConfig && Array.isArray(deviceConfig?.items)) {
              // 获取源设备检查项配置
              const sourceDeviceItems = deviceConfig?.items || []

              tab.items.forEach((deviceGroup: any) => {
                const deviceHiddenItems: string[] = []

                // 获取该设备实际渲染的安检项（在清理前先保存）
                const deviceItems = deviceGroup?.form?.deviceItems || []

                // 遍历源设备检查项配置
                sourceDeviceItems.forEach((sourceItem: any) => {
                  if (!sourceItem || !sourceItem.itemname)
                    return

                  // 在 deviceItems 中查找是否存在该检查项
                  const renderedItem = deviceItems.find((item: any) => item.itemname === sourceItem.itemname)

                  if (!renderedItem) {
                    // 不包含的项，添加到隐藏项
                    deviceHiddenItems.push(sourceItem.itemname)
                  }
                  else if (renderedItem.show === false) {
                    // 包含但是 show 属性为 false 的项，添加到隐藏项
                    deviceHiddenItems.push(sourceItem.itemname)
                  }
                })

                // 只有当该设备有隐藏项时才添加 hiddenItems 属性
                if (deviceHiddenItems.length > 0) {
                  deviceGroup.hiddenItems = deviceHiddenItems
                }
              })
            }
          }
        }
      }
      catch (e) {
        console.warn(`收集分组 ${tabName} 隐藏项时出错:`, e)
      }

      // 只有当 hiddenItems 有值时才添加到当前分组
      if (hiddenItems.length > 0) {
        tab.hiddenItems = hiddenItems
      }
    })
  }

  // 数据清理函数
  const cleanupData = () => {
    completeCheckItemForm.forEach((tab: any) => {
      if (!tab || typeof tab !== 'object')
        return

      const tabType = tab.type || 'normal'

      if (tabType === 'normal') {
        // 普通分组：清理 items 中 value 为 null 或空串的对象
        if (Array.isArray(tab.items)) {
          tab.items = tab.items.filter((item: any) => {
            if (!item || typeof item !== 'object')
              return false
            const value = item.value
            return value !== null && value !== undefined && String(value).trim() !== ''
          })
        }
      }
      else if (tabType === 'device') {
        // 设备分组：清理每个设备的 form 数据，并删除 deviceItems
        if (Array.isArray(tab.items)) {
          tab.items.forEach((deviceGroup: any) => {
            if (deviceGroup?.form && typeof deviceGroup.form === 'object') {
              // 清理 form 中 value 为 null 或空串的属性
              Object.keys(deviceGroup.form).forEach((key) => {
                const value = deviceGroup.form[key]
                if (value === null || value === undefined || String(value).trim() === '') {
                  delete deviceGroup.form[key]
                }
              })

              // 删除 deviceItems 对象
              if (deviceGroup.form.deviceItems) {
                delete deviceGroup.form.deviceItems
              }
            }
          })
        }
      }
    })
  }

  // 执行隐藏项收集和数据清理
  collectHiddenItems()
  cleanupData()

  // 隐患列表的字段也转换为字符串数组（临时数据恢复后为对象数组）
  const normalizedDefectList = Array.isArray(defectList.value)
    ? defectList.value.map((d: any) => ({
        ...d,
        defectPhotos: toIds(d?.defectPhotos),
        processPhotos: toIds(d?.processPhotos),
      }))
    : []
  let location = ''
  // 获取安检单上传时的人员定位
  if (safeConfig.value?.needLocation) {
    // 使用 Promise 包装回调
    location = await new Promise((resolve) => {
      mobileUtil.execute({
        param: {},
        funcName: 'getLocationResult',
        callbackFunc: (res: any) => {
          console.log('获取位置结果:', res)
          if (res?.status === 'success') {
            try {
              const locationResult = JSON.parse(res.data.location)
              if (
                typeof locationResult.longitude === 'number'
                && typeof locationResult.latitude === 'number'
              ) {
                resolve(`${locationResult.longitude},${locationResult.latitude}`)
                return
              }
            }
            catch (err) {
              console.error('定位数据解析失败:', err)
            }
          }
          resolve('') // 获取失败返回空字符串
        },
      })
    })
  }
  return {
    planId: planId.value,
    checkId: checkId.value,
    checkItemForm: completeCheckItemForm,
    checkItemFormJson: JSON.stringify(completeCheckItemForm),
    defectList: normalizedDefectList,
    extData: extData.value,
    ...(location && { location }),
    checkerInfo: {
      id: checkerInfo.id,
      name: checkerInfo.name,
      deps: checkerInfo.deps,
      depids: checkerInfo.depids,
      orgs: checkerInfo.orgs,
      orgid: checkerInfo.orgid,
      ename: checkerInfo.ename,
    },
    onSiteDate: onSiteDate.value,
    safeCheckVersion: safeCheckVersion.value,
    safeCheckTypeId: safeCheckTypeId.value,
    userinfoId: userinfoId.value,
    commonId: commonId.value,
    bonusData: route?.query?.bonusData ? JSON.parse(route.query.bonusData as any) : {},
    draftType: draftType.value,
    changeTableId: safecheckStore.getChangeTableId(),
    verificationCodeKey: verificationCodeKey.value,
    photoStats: { ...photoStats },
  }
}

// 将 f_safe_content 合并到 templateJson（仅依据 tabName 与 itemname 相同匹配），仅回填 value、f_defect_paths、processMode、f_process_paths
function mergeSafeContentByTabAndType(templateJson: any[], safeContent: any[]): any[] {
  if (!Array.isArray(templateJson))
    return []

  const groupKey = (tabName: any) => String(tabName ?? '')

  // 构建 f_safe_content 的分组映射
  const safeGroupMap = new Map<string, any>()
  ;(Array.isArray(safeContent) ? safeContent : []).forEach((g: any) => {
    if (!g)
      return
    safeGroupMap.set(groupKey(g.tabName), g)
  })

  // 遍历模板分组，按组匹配后逐项赋值
  templateJson.forEach((tplGroup: any, index) => {
    const key = groupKey(tplGroup?.itemname)
    const scGroup = safeGroupMap.get(key)
    if (!scGroup)
      return

    const tplItems = Array.isArray(tplGroup?.items) ? tplGroup.items : null
    const scItems = Array.isArray(scGroup?.items) ? scGroup.items : null
    if (!tplItems || !scItems)
      return

    // 检查是否为设备类型，需要特殊处理
    if (scGroup.type === 'device') {
      // 设备类型：将设备数据组织后传入到模板中
      const deviceData = scItems.map((device: any) => {
        const deviceItem = {
          deviceId: device.deviceId || `device_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
          f_devices_type: device.deviceType,
          ...device.form,
          groupName: tplGroup?.itemname, // 添加分组名称，用于隐患列表
          items: device.items || [],
        }

        // 处理设备内的每个检查项
        if (Array.isArray(device.items)) {
          deviceItem.items = device.items.map((item: any) => ({
            itemname: item.itemName || '未知项目',
            value: item.value || '',
            f_process_mode: item.processMode || '',
            f_defect_paths: Array.isArray(item.f_defect_paths) ? [...item.f_defect_paths] : [],
            f_process_paths: Array.isArray(item.f_process_paths) ? [...item.f_process_paths] : [],
            f_handling_type: item.f_handling_type || null,
            f_dispatch_dept: item.f_dispatch_dept || null,
            f_dispatch_dept_name: item.f_dispatch_dept_name || null,
            f_repairman: item.f_repairman || null,
            f_repairman_name: item.f_repairman_name || null,
          }))
        }
        return deviceItem
      })

      // 将设备数据设置到模板中，供 GasDevice 组件使用
      tplGroup.deviceData = deviceData
    }
    else {
      // 普通安检项：按原有逻辑处理
      const safeItemMap = new Map<string, any>()
      scItems.forEach((it: any) => {
        if (it && typeof it === 'object' && 'itemName' in it)
          safeItemMap.set(String(it.itemName), it)
      })

      tplItems.forEach((tItem: any) => {
        const scItem = safeItemMap.get(String(tItem?.itemname))
        if (!scItem)
          return
        // 回填 value
        if (scItem.value !== undefined && scItem.value !== null) {
          tItem.value = Array.isArray(scItem.value) ? [...scItem.value] : scItem.value
          if (['radio', 'selector', 'checkbox', 'multipleSelect'].includes(tItem.type)) {
            const opt = tItem.options.find(option => option.data === tItem.value)
            if (opt) {
              tItem.f_is_defect = opt.aging ? true : opt.isdefect
              tItem.aging = opt.aging
              tItem.f_handle_date = opt.f_handle_date
              tItem.level = opt.level
              tItem.defectdes = opt.defectdes
              tItem.f_process_mode = opt.processMode
            }
          }
        }
        if (scItem.f_is_defect !== undefined) {
          tItem.f_is_defect = scItem.f_is_defect
        }
        if (scItem.aging !== undefined) {
          tItem.aging = scItem.aging
        }
        if (scItem.f_handle_date !== undefined) {
          tItem.f_handle_date = scItem.f_handle_date
        }
        if (scItem.level !== undefined) {
          tItem.level = scItem.level
        }
        if (scItem.defectdes !== undefined) {
          tItem.defectdes = scItem.defectdes
        }
        // 回填处理方式
        if (scItem.processMode !== undefined) {
          tItem.f_process_mode = scItem.processMode
        }
        // 回填隐患图片
        if (Array.isArray(scItem.f_defect_paths)) {
          tItem.f_defect_paths = [...scItem.f_defect_paths]
        }

        // 回填处理图片
        if (Array.isArray(scItem.f_process_paths)) {
          tItem.f_process_paths = [...scItem.f_process_paths]
        }

        // 回填转维修相关数据
        if (scItem.f_handling_type !== undefined) {
          tItem.f_handling_type = scItem.f_handling_type
        }
        if (scItem.f_dispatch_dept !== undefined) {
          tItem.f_dispatch_dept = scItem.f_dispatch_dept
        }
        if (scItem.f_dispatch_dept_name !== undefined) {
          tItem.f_dispatch_dept_name = scItem.f_dispatch_dept_name
        }
        if (scItem.f_repairman !== undefined) {
          tItem.f_repairman = scItem.f_repairman
        }
        if (scItem.f_repairman_name !== undefined) {
          tItem.f_repairman_name = scItem.f_repairman_name
        }

        valueChange(tItem, index, null)
      })
    }
  })

  return templateJson
}

// 保存临时数据的函数
async function saveDraftData() {
  // 自定义校验
  let customRes: any
  if (safeConfig.value?.customLogic) {
    customRes = await runLogic(safeConfig.value?.customLogic, { serverStartTime: onSiteDate.value }, 'af-safecheck')
    if (customRes && customRes?.code !== 200 && customRes?.msg) {
      showToast(customRes?.msg || '自定义校验失败')
      return
    }
  }

  if (safeConfig.value?.isTimer && !isReadOnly.value) {
    const limitTime = safeConfig.value?.limitTime || 10
    if (customRes?.timeLimitCheck === 'fail' && elapsedSeconds.value < (limitTime * 60)) {
      showToast(`安检时间必须大于${limitTime}分钟`)
      return
    }
  }

  showConfirmDialog({
    title: '确认保存',
    message: '是否保存当前未提交数据？',
    closeOnPopstate: false,
  }).then(async () => {
    // 设置保存并返回标志，防止触发路由离开拦截
    isSavingAndReturning.value = true

    try {
      // 如果正在录音，先停止录音
      if (recordSwitch.value && isRecording.value) {
        await stopRecordingSafely()
        console.log('保存时停止录音')
      }

      // 如果有最新录音数据，直接提交录音数据
      if (recordSwitch.value && latestRecordData.value) {
        await uploadRecording()
      }
      // 保存临时数据
      const param = {
        id: checkId.value,
        draftType: draftType.value,
        f_temporary_storage: await organizeSubmitData(),
        f_temporary_date: formatNow(),
      }
      await runLogic('saveDraft', param, 'af-safecheck')
      showSuccessToast('临时数据保存成功！')
      goBack()
      clear()
    }
    catch (error) {
      console.error('保存临时数据失败:', error)
      showFailToast('临时数据保存失败！')
    }
    finally {
      // 重置标志
      isSavingAndReturning.value = false
    }
  })
}

// 提交表单时的处理逻辑
async function onSubmit() {
  try {
    // 验证所有表单并更新错误计数
    const { hasErrors, firstErrorMessage } = await validateAllForms()

    if (hasErrors) {
      // 找到第一个有错误的标签页并跳转
      if (errorCounts.userInfo > 0) {
        activeTabIndex.value = 0
      }
      else {
        const firstErrorIndex = errorCounts.tabs.findIndex(count => count > 0)
        if (firstErrorIndex >= 0)
          activeTabIndex.value = firstErrorIndex + 1 // +1 因为第0个是用户信息
        else if (errorCounts.hiddenDangers > 0)
          activeTabIndex.value = actions.value.length - 2
        else if (errorCounts.photoSignature > 0)
          activeTabIndex.value = actions.value.length - 1
      }

      // 显示具体的错误信息，而不是通用提示
      showFailToast(firstErrorMessage || '请填写所有的必填项')
      return
    }

    // 用户确认提交，组织提交数据
    const submitData = await organizeSubmitData()
    console.log('提交数据:', submitData)

    // 自定义校验
    let customRes: any
    if (safeConfig.value?.customLogic) {
      customRes = await runLogic(safeConfig.value?.customLogic, { paperData: submitData, changeTableData: safecheckStore.getChangeTableId(), serverStartTime: onSiteDate.value }, 'af-safecheck')
      if (customRes && customRes?.code !== 200 && customRes?.msg) {
        showToast(customRes?.msg || '自定义校验失败')
        return
      }
    }

    if (safeConfig.value?.isTimer && !isReadOnly.value) {
      const limitTime = safeConfig.value?.limitTime || 10
      if (customRes?.timeLimitCheck === 'fail' && elapsedSeconds.value < (limitTime * 60)) {
        showToast(`安检时间必须大于${limitTime}分钟`)
        return
      }
    }

    // 开启施封进行校验
    if (safeConfig.value?.isseal && !isReadOnly.value) {
      const uid = await sealCheck()
      const userInfo = userDetail.value.userinfo
      if (uid !== userInfo.f_uid) {
        showToast('该铅封信息与绑定的铅封信息不匹配，请重新尝试！')
        return
      }
    }
    // 如果所有表单校验通过，则弹出确认框
    showConfirmDialog({
      title: '安检单填写',
      message: '请确认是否提交？',
    })
      .then(async () => {
        try {
          // 如果开启了录音且正在录音，先停止录音
          if (recordSwitch.value && isRecording.value) {
            await stopRecordingSafely()
            console.log('录音已停止')
          }

          // 如果开启了录音且有录音文件，先上传录音
          if (recordSwitch.value && latestRecordData.value) {
            await uploadRecording()
          }

          // 上传安检单数据
          await submitSecurityCheckData(submitData)
        }
        catch (error) {
          console.error('提交过程中发生错误:', error)
          showFailToast(`提交失败: ${error.message || '未知错误'}`)
        }
      })
      .catch(() => {
        // 用户取消提交
      })
  }
  catch (error) {
    // 如果有表单校验未通过，则提示用户
    console.error('表单校验未通过:', error)
    showFailToast('请填写所有的必填项！！！')
  }
}

// nfc
function sealCheck() {
  return new Promise((resolve, reject) => {
    mobileUtil.execute({
      funcName: 'startNfcScan',
      param: {},
      callbackFunc: (res: any) => {
        console.log('nfc结果:------', res)
        if (res && res.status === 'success') {
          resolve(res.data.id)
        }
        else {
          showToast(res?.message || 'NFC 功能不可用')
        }
      },
    })
  })
}

// 上传录音文件
function uploadRecording() {
  if (latestRecordData.value) {
    const filePath = latestRecordData.value
    mobileUtil.execute({
      funcName: 'queueUpload',
      param: {
        resUploadMode: 'server',
        pathKey: 'Default',
        formType: 'file',
        useType: 'Default',
        resUploadStock: '1',
        filename: filePath.split('/').pop(),
        f_operator: 'server',
        imgPath: filePath,
        urlPath: `/api/${import.meta.env.VITE_APP_SYSTEM_NAME}/resource/upload`,
        commonId: commonId.value ?? '',
      },
      callbackFunc: (resp: any) => {
        if (resp.data && resp.data.enqueued) {
          console.log('录音加入队列成功', resp)
        }
        else {
          console.log('录音加入队列失败', resp)
        }
      },
    })
  }
}

// 测试使用
// function lookUpload() {
//   mobileUtil.execute({ funcName: 'queueList', param: {}, callbackFunc: res => console.log('list:---', res) })
// }

// 提交安检单数据
function submitSecurityCheckData(submitData: any) {
  // 这个是异步上传
  const f_user_name = userInfoRef.value.getData()?.f_user_name
  const f_address = userInfoRef.value.getData()?.f_address
  const f_user_phone = userInfoRef.value.getData()?.f_user_phone
  const f_operator = checkerInfo.name
  mobileUtil.execute({
    funcName: 'flutterQueue',
    param: {
      method: 'POST',
      url: `/api/${import.meta.env.VITE_APP_SYSTEM_NAME}/logic/submitSecurityCheckData`,
      payload: submitData,
      info: JSON.stringify({ flagInfo: '安检单', f_user_name, f_address, f_user_phone, f_operator }),
      commonId: commonId.value ?? '',
    },
    callbackFunc: (res: any) => {
      console.log('看看结果----', res)
      if (res.data && res.data.enqueued) {
        showSuccessToast('提交成功')
        // 提交成功后返回
        goBack()
        clear()
      }
      else {
        showFailToast('提交失败')
      }
    },
  })

  // 这个是同步上传
  // return new Promise((resolve, reject) => {
  //   runLogic('submitSecurityCheckData', submitData).then((result: any) => {
  //     console.log('提交成功', result)
  //     if (result.status === 200) {
  //       showSuccessToast('提交成功')
  //       // 提交成功后返回
  //       router.back()
  //       resolve(result)
  //     }
  //     else {
  //       showFailToast('提交失败')
  //       reject(new Error('提交失败'))
  //     }
  //   }).catch((error) => {
  //     showFailToast('提交失败')
  //     reject(error)
  //   })
  // })
}

// 更新actions数据的函数
function updateActions() {
  // 清空actions，保留第一个"用户信息"选项
  actions.value = [{ text: '用户信息', value: 0, icon: '', errorCount: 0 }]
  // 从config.data中获取itemname，添加到actions中
  config.data.forEach((item, index) => {
    actions.value.push({
      text: item.itemname,
      value: index + 1, // 索引+1，因为第1个是用户信息
      icon: '', // 添加空的icon属性以满足类型要求
      errorCount: 0,
    })
  })
  actions.value.push({
    text: '整体隐患',
    value: actions.value.length, // 倒数第二
    icon: '', // 添加空的icon属性以满足类型要求
    errorCount: 0,
  })
  actions.value.push({
    text: '拍照签名',
    value: actions.value.length, // 最后一个
    icon: '', // 添加空的icon属性以满足类型要求
    errorCount: 0,
  })
}

// 更新actions中的错误计数的计算属性
const updatedActions = computed(() => {
  return actions.value.map((action, index) => {
    if (index === 0) {
      // 用户信息
      return {
        ...action,
        errorCount: errorCounts.userInfo,
      }
    }
    else if (index === actions.value.length - 2) {
      // 整体隐患
      return {
        ...action,
        errorCount: errorCounts.hiddenDangers,
      }
    }
    else if (index === actions.value.length - 1) {
      // 拍照签名
      return {
        ...action,
        errorCount: errorCounts.photoSignature,
      }
    }
    else {
      // 其他标签页，需要检查是否为slot类型
      const configIndex = index - 1 // 因为第0个是用户信息
      const configItem = config.data[configIndex]

      // 如果是slot类型，使用正常的错误计数
      if (configItem && configItem.type === 'slot') {
        return {
          ...action,
          errorCount: errorCounts.tabs[configIndex] || 0,
        }
      }

      // 其他类型使用正常的错误计数
      return {
        ...action,
        errorCount: errorCounts.tabs[configIndex] || 0,
      }
    }
  })
})

function valueChange(currentVal, pIndex, payload = null) {
  checkItemChange(currentVal, pIndex, payload)
}

// 工具：确保 tab 存在
function ensureTab(tabName: string, type: 'normal' | 'device') {
  let tab = (checkItemForm.value || []).find((t: any) => t.tabName === tabName && (t.type || 'normal') === type)
  if (!tab) {
    tab = { tabName, type, items: [] }
    checkItemForm.value.push(tab)
  }
  if (!Array.isArray(tab.items))
    tab.items = []
  return tab
}

// 若指定 tab 的 items 为空，则从顶层移除该 tab
function removeTabIfEmpty(tabName: string, type: 'normal' | 'device') {
  const idx = (checkItemForm.value || []).findIndex((t: any) => t.tabName === tabName && (t.type || 'normal') === type)
  if (idx !== -1) {
    const tab = checkItemForm.value[idx]
    if (!tab || !Array.isArray(tab.items) || tab.items.length === 0) {
      checkItemForm.value.splice(idx, 1)
    }
  }
}

// 当用户填写内容后 如果内容和原始默认值不相同 将填写的内容进行单独存储
function checkItemChange(currentVal, pIndex, payload) {
  console.log('123---', JSON.stringify(currentVal), JSON.stringify(pIndex), JSON.stringify(payload))
  const isDevice = payload && typeof payload === 'object' && payload.deviceId
  if (!isDevice) {
    // 普通安检项：tabName 取配置分组名，聚合到 type='normal' 的 tab 下
    const tabName = config.data[pIndex]?.itemname || '未命名'
    // 确保普通项的 tab 存在
    const tab = ensureTab(tabName, 'normal')
    // 同名表单项去重（按 itemName）
    const idx = tab.items.findIndex((i: any) => i.itemName === currentVal.itemname)
    if (idx !== -1)
      tab.items.splice(idx, 1)
    // 记录当前项的值与附属字段（处理方式、隐患照片）
    const rec: any = { itemName: currentVal.itemname, value: currentVal.value }
    if (Array.isArray(currentVal.value) && currentVal.value.length > 0)
      rec.value = getPhotosFromAny(currentVal, 'value')
    if (currentVal.f_process_mode)
      rec.processMode = currentVal.f_process_mode
    if (currentVal.f_defect_paths && currentVal.f_defect_paths.length > 0)
      rec.f_defect_paths = getPhotosFromAny(currentVal, 'f_defect_paths')
    if (currentVal.f_process_mode && currentVal.f_process_mode === '现场处理' && currentVal.f_process_paths && currentVal.f_process_paths.length > 0)
      rec.f_process_paths = getPhotosFromAny(currentVal, 'f_process_paths')
    if (currentVal.f_is_defect)
      rec.f_is_defect = currentVal.f_is_defect
    if (currentVal.aging)
      rec.aging = currentVal.aging
    if (currentVal.f_handle_date)
      rec.f_handle_date = currentVal.f_handle_date
    if (currentVal.level)
      rec.level = currentVal.level
    if (currentVal.defectdes)
      rec.defectdes = currentVal.defectdes
    if (currentVal.f_handling_type)
      rec.f_handling_type = currentVal.f_handling_type
    if (currentVal.f_dispatch_dept)
      rec.f_dispatch_dept = currentVal.f_dispatch_dept
    if (currentVal.f_dispatch_dept_name)
      rec.f_dispatch_dept_name = currentVal.f_dispatch_dept_name
    if (currentVal.f_repairman)
      rec.f_repairman = currentVal.f_repairman
    if (currentVal.f_repairman_name)
      rec.f_repairman_name = currentVal.f_repairman_name
    // 过滤默认选中且未更改的情形，仅保留用户有效输入
    let isDefault = false
    if (['selector', 'radio'].includes(currentVal.type)) {
      const def = Array.isArray(currentVal.options) ? currentVal.options.find((o: any) => o.isdefault) : null
      if (def)
        isDefault = (currentVal.value === def.data)
    }
    const hasValue = rec.value != null && String(rec.value).trim() !== ''
    if (hasValue && !isDefault)
      tab.items.push(rec)
    // 若标记为隐患，同步生成/更新 defectList 项
    if (currentVal)
      dealDefectItem(currentVal, tabName)
    console.log('defect------', defectList.value)
    // 若该普通 tab 无数据则移除
    removeTabIfEmpty(tabName, 'normal')
    console.log('checkitem------', checkItemForm.value)
    return
  }
  // 设备项：聚合到 设备检查 -> 设备分组
  const deviceId = payload.deviceId
  const deviceType = payload.deviceType
  const groupName = payload.groupName // 使用动态分组名称
  // 确保设备聚合的顶层 tab 存在（使用动态分组名称）
  const dTab = ensureTab(groupName, 'device')
  console.warn('>>>dTab', JSON.stringify(dTab))
  // 按 deviceId 定位设备分组
  let group = dTab.items.find((i: any) => i.deviceId === deviceId)
  if (!group) {
    // 创建设备分组容器
    group = { deviceId, deviceType, groupName, items: [] }
    dTab.items.push(group)
  }
  if (!Array.isArray(group.items))
    group.items = []
  const gitems = group.items
  // 设备分组中对同名表单项去重
  const gidx = gitems.findIndex((i: any) => i.itemName === currentVal.itemname)
  if (gidx !== -1)
    gitems.splice(gidx, 1)
  // 写入当前设备项记录
  const grec: any = { itemName: currentVal.itemname, value: currentVal.value }
  if (Array.isArray(currentVal.value) && currentVal.value.length > 0)
    grec.value = getPhotosFromAny(currentVal, 'value')
  if (currentVal.f_process_mode)
    grec.processMode = currentVal.f_process_mode
  if (currentVal.f_handling_type)
    grec.f_handling_type = currentVal.f_handling_type
  if (currentVal.f_dispatch_dept)
    grec.f_dispatch_dept = currentVal.f_dispatch_dept
  if (currentVal.f_dispatch_dept_name)
    grec.f_dispatch_dept_name = currentVal.f_dispatch_dept_name
  if (currentVal.f_repairman)
    grec.f_repairman = currentVal.f_repairman
  if (currentVal.f_repairman_name)
    grec.f_repairman_name = currentVal.f_repairman_name
  if (currentVal.f_defect_paths && currentVal.f_defect_paths.length > 0)
    grec.f_defect_paths = getPhotosFromAny(currentVal, 'f_defect_paths')
  if (currentVal.f_process_mode && currentVal.f_process_mode === '现场处理' && currentVal.f_process_paths && currentVal.f_process_paths.length > 0)
    grec.f_process_paths = getPhotosFromAny(currentVal, 'f_process_paths')
  // 过滤默认选中且未更改的情形
  let gDefault = false
  if (['selector', 'radio'].includes(currentVal.type)) {
    const def = Array.isArray(currentVal.options) ? currentVal.options.find((o: any) => o.isdefault) : null
    if (def)
      gDefault = (currentVal.value === def.data)
  }
  const gHasValue = grec.value != null && String(grec.value).trim() !== ''
  if (gHasValue && !gDefault)
    gitems.push(grec)
  // 若标记为隐患，同步生成/更新 defectList（携带 deviceId）
  if (currentVal)
    // 设备隐患的 defectdes 使用设备类型作为上级名称（如 报警器），而不是固定的"设备检查"
    dealDefectItem(currentVal, deviceType, deviceId)
  console.log('defect------', defectList.value)
  // 若当前设备分组已无数据，移除该分组
  if (!Array.isArray(group.items) || group.items.length === 0) {
    const gi = dTab.items.findIndex((i: any) => i.deviceId === deviceId)
    if (gi !== -1) {
      dTab.items.splice(gi, 1)
    }
  }
  // 若设备检查 tab 无任何分组，移除该 tab
  if (!Array.isArray(dTab.items) || dTab.items.length === 0) {
    removeTabIfEmpty(groupName, 'device')
  }
  console.warn('>>> checkItemForm', JSON.stringify(checkItemForm.value))
}

// 添加隐患项到隐患列表
function dealDefectItem(currentVal, tabName, deviceId = undefined) {
  if (currentVal.f_is_defect) {
    const newDefectPaths = getPhotosFromAny(currentVal, 'f_defect_paths')
    const newProcessPaths = getPhotosFromAny(currentVal, 'f_process_paths')
    const defectItem = {
      tabName, // 父项名称
      itemName: currentVal.itemname, // 安检项名称
      itemValue: currentVal.value, // 安检项内容
      processMode: currentVal.f_process_mode, // 处理方式
      defectPhotos: newDefectPaths || [], // 隐患照片
      processPhotos: newProcessPaths || [], // 处理照片
      aging: currentVal.aging, // 隐患时效
      f_handle_date: currentVal.f_handle_date, // 隐患时效
      level: currentVal.level, // 隐患等级
      defectdes: `${tabName}-${currentVal.defectdes}`, // 隐患描述
      type: 'SecurityFormItem',
      deviceId,
      f_handling_type: currentVal.f_handling_type,
      f_dispatch_dept: currentVal.f_dispatch_dept,
      f_dispatch_dept_name: currentVal.f_dispatch_dept_name,
      f_repairman: currentVal.f_repairman,
      f_repairman_name: currentVal.f_repairman_name,
    }

    // 检查是否已存在相同的隐患项
    const existingIndex = defectList.value.findIndex(item =>
      item.tabName === defectItem.tabName
      && item.itemName === defectItem.itemName
      && (item.deviceId || undefined) === (deviceId || undefined),
    )

    if (existingIndex >= 0) {
      defectList.value[existingIndex] = defectItem
    }
    else {
      defectList.value.push(defectItem)
    }
  }
  if (!currentVal.f_is_defect) {
    // 若未标记为隐患，从 defectList 中移除
    const existingIndex = defectList.value.findIndex(item =>
      item.tabName === tabName
      && item.itemName === currentVal.itemname,
    )
    if (existingIndex >= 0) {
      defectList.value.splice(existingIndex, 1)
    }
  }
}

// 获取 van-tabs 的实例与滚动容器引用
const tabsRef = ref(null)
const tabsContainerRef = ref<HTMLElement | null>(null)

function slideLeft() {
  if (activeTabIndex.value === 0)
    activeTabIndex.value = actions.value.length - 1
  else
    activeTabIndex.value = activeTabIndex.value - 1
  if (tabsRef.value)
    (tabsRef.value as any).scrollTo(activeTabIndex.value)
  // 滚动到顶部
  try {
    const el = tabsContainerRef.value
    if (el)
      el.scrollTo({ top: 0, behavior: 'auto' })
  }
  catch {}
}

function slideRight() {
  if (activeTabIndex.value === actions.value.length - 1)
    activeTabIndex.value = 0
  else
    activeTabIndex.value = activeTabIndex.value + 1
  if (tabsRef.value)
    (tabsRef.value as any).scrollTo(activeTabIndex.value)
  // 滚动到顶部
  try {
    const el = tabsContainerRef.value
    if (el)
      el.scrollTo({ top: 0, behavior: 'auto' })
  }
  catch {}
}

// 气泡弹出层按钮状态
const tabStatus = ref(false)

function PageSwitch(event: any) {
  // 设置当前选中的tab索引
  activeTabIndex.value = event.value
  if (tabsRef.value)
    (tabsRef.value as any).scrollTo(event.value)
  // 切换后滚动到顶部
  try {
    const el = tabsContainerRef.value
    if (el)
      el.scrollTo({ top: 0, behavior: 'auto' })
  }
  catch {}
}

// 气泡弹出层的图标
const popoverIcon = computed(() => {
  return tabStatus.value ? 'arrow-up' : 'arrow-down'
})
// 底部按钮的样式（已移除，现在使用独立的保存按钮和图标翻页按钮）

// 计算 offset-top 值
const offsetTop = computed(() => {
  return (window as any).__MICRO_APP_ENVIRONMENT__ ? (getComputedStyle(document.documentElement).getPropertyValue('--system-header-height') || 56) : 0
})

// 菜单位置计算
const menuPosition = computed(() => {
  // 气泡直径和菜单宽度
  const bubbleSize = 55
  const menuWidth = 140

  // 预估菜单高度（避免覆盖气泡）
  const actionsCount = Array.isArray(floatingBubbleActions.value) ? floatingBubbleActions.value.length : 0
  const itemHeight = 44
  const verticalPadding = 16
  const minHeight = 44
  const menuHeightEstimate = Math.max(minHeight, actionsCount * itemHeight + verticalPadding)
  const gap = 8
  const edgeMargin = 20 // 距离屏幕右侧与左侧的安全边距
  const bubbleHorizontalGap = 12 // 菜单与小工具图标的额外水平间距

  // 水平位置：菜单居中对齐气泡（并做边界保护）
  const menuLeftBase = offset.value.x - (menuWidth / 2) + (bubbleSize / 2)
  const menuLeft = menuLeftBase - bubbleHorizontalGap
  const safeMenuLeft = Math.max(edgeMargin, Math.min(window.innerWidth - menuWidth - edgeMargin, menuLeft))

  // 垂直位置：优先显示在气泡上方，空间不足时显示在气泡下方
  const canShowAbove = (offset.value.y - menuHeightEstimate - gap) > 0
  const topWhenAbove = offset.value.y - menuHeightEstimate - gap
  const topWhenBelow = offset.value.y + bubbleSize + gap

  return {
    left: `${safeMenuLeft}px`,
    top: `${canShowAbove ? topWhenAbove : topWhenBelow}px`,
    position: 'fixed' as const,
    zIndex: 2001,
  }
})

// 浮动气泡点击事件
function onFloatingBubble() {
  floatingBubbleStatus.value = !floatingBubbleStatus.value
}

async function floatingBubbleSwitch(action) {
  // 返回首页固定为路由回退
  if (action.title === '返回上级') {
    goBack()
    clear()
  }
  else if (action.title === '清空保存数据') {
    const param = {
      id: checkId.value,
      f_temporary_storage: null,
      f_temporary_date: null,
    }
    try {
      await runLogic('saveDraft', param, 'af-safecheck')
    }
    catch {}
    // 先清理本地状态，立即更新界面
    resetLocalState()
    isDraft.value = 'false'
    // 重新拉取表单配置并进入编辑模式（跳过确认弹窗）
    await reloadNewForm()
  }
  // 其他路由固定为路由页面跳转携带参数
  else if (action.routerName) {
    await router.push({
      path: action.routerName,
      query: {
        paperId: safeCheckPaperId.value,
        f_userinfo_id: userinfoId.value,
        isReadOnly: isReadOnly.value ? 'true' : 'false',
      },
    })
  }
  else {
    showFailToast('功能开发中')
  }
  // 关闭气泡
  floatingBubbleStatus.value = false
}

// 添加点击外部关闭菜单的功能
const stopFloatingBubbleWatch = watch(() => floatingBubbleStatus.value, (newVal) => {
  if (newVal) {
    // 延迟添加事件，避免同一次点击立即关闭
    if (bubbleClickTimer.value !== null)
      clearTimeout(bubbleClickTimer.value)
    bubbleClickTimer.value = window.setTimeout(() => {
      bubbleClickTimer.value = null
      document.addEventListener('click', handleOutsideClick)
    }, 10)
  }
  else {
    if (bubbleClickTimer.value !== null) {
      clearTimeout(bubbleClickTimer.value)
      bubbleClickTimer.value = null
    }
    document.removeEventListener('click', handleOutsideClick)
  }
})

// 监听标签索引变化，确保内容区回到顶部
const stopActiveTabWatch = watch(() => activeTabIndex.value, async () => {
  await nextTick()
  try {
    const el = tabsContainerRef.value
    if (el)
      el.scrollTo({ top: 0, behavior: 'auto' })
  }
  catch {}
})

function handleOutsideClick(e) {
  const bubble = document.querySelector('.van-floating-bubble')
  const menu = document.querySelector('.bubble-menu-container')

  // 如果点击不在气泡或菜单内，则关闭菜单
  if (bubble && !bubble.contains(e.target) && menu && !menu.contains(e.target))
    floatingBubbleStatus.value = false
}

// 获取指定插槽的指定函数
function getSlotMethod(slotName: string, functionName: string) {
  if (!slotRefs?.value?.length)
    return null

  // 找到对应 slot index
  const index = config?.data?.findIndex((item: any) => item.slotName === slotName)

  if (index < 0)
    return null

  const refInstance = slotRefs.value[index]

  if (!refInstance)
    return null

  const fn = refInstance[functionName]

  if (typeof fn !== 'function')
    return null

  return fn.bind(refInstance) // 🔥 关键：绑定 this
}

function callSlotMethod(
  slotName: string,
  functionName: string,
  ...args: any[]
) {
  const fn = getSlotMethod(slotName, functionName)

  if (!fn) {
    console.warn(`未找到方法: ${slotName}.${functionName}`)
    return
  }

  return fn(...args)
}

/**
 * 判断是否为合法日期，并统一格式化为 yyyy-MM-dd
 */
function normalizeDate(value) {
  if (value === null || value === undefined || value === '') {
    return null
  }

  // Date对象 / 时间戳
  if (value instanceof Date || typeof value === 'number') {
    const m = moment(value)

    return m.isValid()
      ? m.format('YYYY-MM-DD')
      : null
  }

  // 字符串处理
  if (typeof value === 'string') {
    const formats = [
      // =========================
      // 标准日期
      // =========================
      'YYYY-MM-DD',
      'YYYY/MM/DD',
      'YYYY.MM.DD',
      'YYYY_M_D',
      'YYYY-M-D',
      'YYYY/M/D',
      'YYYY.M.D',

      // =========================
      // 中文日期
      // =========================
      'YYYY年MM月DD日',
      'YYYY年M月D日',
      'YYYY年MM月D日',
      'YYYY年MM月',
      'YYYY年M月DD日',
      'YYYY年M月',

      // =========================
      // 紧凑格式
      // =========================
      'YYYYMMDD',

      // =========================
      // 带时间
      // =========================
      'YYYY-MM-DD HH:mm:ss',
      'YYYY/MM/DD HH:mm:ss',
      'YYYY.MM.DD HH:mm:ss',

      'YYYY-M-D H:m:s',
      'YYYY/M/D H:m:s',

      'YYYY-MM-DD HH:mm',
      'YYYY/MM/DD HH:mm',
      'YYYY.MM.DD HH:mm',

      // 中文时间
      'YYYY年MM月DD日 HH:mm:ss',
      'YYYY年M月D日 HH:mm:ss',
      'YYYY年MM月DD日 HH:mm',
      'YYYY年M月D日 HH:mm',

      // =========================
      // ISO8601
      // =========================
      'YYYY-MM-DDTHH:mm:ss',
      'YYYY-MM-DDTHH:mm:ssZ',
      'YYYY-MM-DDTHH:mm:ss.SSSZ',

      // =========================
      // 欧美格式（谨慎使用）
      // =========================
      'MM/DD/YYYY',
      'M/D/YYYY',
      'DD/MM/YYYY',
      'D/M/YYYY',

      // =========================
      // 英文月份
      // =========================
      'MMM DD YYYY',
      'MMMM DD YYYY',
      'DD MMM YYYY',
      'DD MMMM YYYY',
    ]

    const m = moment(value, formats, true)

    return m.isValid()
      ? m.format('YYYY-MM-DD')
      : null
  }

  return null
}
// 扁平ai识别数据
function flattenProperties(properties, keyMap = {}, singleData = null) {
  console.warn('keyMap', JSON.stringify(keyMap))
  const result = {}

  if (!Array.isArray(properties))
    return result

  properties.forEach((item) => {
    if (!item || !item.key)
      return

    // ❌ 跳过可信度
    if (item.key === '可信度')
      return

    const rawKey = item.key
    let newKey
    let newValue

    // ✅ 特殊：名称
    if (item.key === '名称') {
      newKey = item.value
      newValue = singleData
      callSlotMethod('gasDevice', 'addDeviceByName', newKey)
    }
    else {
      newKey = item.key
      newValue = item.value
      if (typeof newValue === 'string') {
        const match = normalizeDate(newValue)
        if (match) {
          newValue = match
        }
      }
    }

    // ✅ keyMap 映射（只改 key）
    if (keyMap[newKey] !== undefined) {
      newKey = keyMap[newKey]
    }

    // ✅ 覆盖写入
    // 返回值保留原始 key，便于后续回更到 properties
    result[newKey] = {
      value: newValue ?? '',
      rawKey,
    }
  })

  return result
}
/**
 * // 判断一个json是否有指定的key。并且key对应的value是否有效
 * @param obj
 * @param key
 */
function hasKeyAndValue(obj: any, key: string): boolean {
  if (!obj || typeof obj !== 'object')
    return false

  // 1. 判断 key 是否存在
  if (!Object.prototype.hasOwnProperty.call(obj, key)) {
    return false
  }

  const raw = obj[key]
  const value = (raw && typeof raw === 'object' && 'value' in raw) ? raw.value : raw

  // 2. 判断 value 是否“有值”
  if (value === null || value === undefined)
    return false

  // 空字符串
  if (typeof value === 'string' && value.trim() === '')
    return false

  // 空数组
  if (Array.isArray(value) && value.length === 0)
    return false

  // 空对象
  if (typeof value === 'object' && !Array.isArray(value) && Object.keys(value).length === 0) {
    return false
  }

  return true
}
// 同步气表照片
async function successFileList(data, images) {
  showConfig.value = false
  console.warn('>>>> successFileList', JSON.stringify(data), JSON.stringify(images))
  images[0].photo_name = images[0].name

  const value = [{
    photo_name: images[0].f_real_name,
    url: images[0].url,
  }]
  let device = null
  const properties = data.properties
  properties.forEach((item) => {
    if (item.key === '名称') {
      // item.value = '灶具'
      device = item.value
    }
  })
  // const isQB = device === '气表' || device === '燃气表'
  const newData = flattenProperties(data.properties, aiKeyToFieldMap.value, value)
  const deviceTab = callSlotMethod('gasDevice', 'getDeviceDetailsByName', device)
  // 测试用气环境同步数据
  // newData['是否漏气'] = { value: '是', rawKey: '是否漏气' }
  // newData['防盗卡扣'] = { value: '丢失', rawKey: '防盗卡扣' }
  console.log('》》》newData', JSON.stringify(newData))
  console.log('》》》device', JSON.stringify(device))
  console.log('》》》deviceTab', JSON.stringify(deviceTab))
  let found = false // ⭐ 是否找到标记
  const defectRecords: any[] = []
  const matchedFieldInfos: SyncedFieldInfo[] = [] // 记录本次同步修改了哪些属性及对应tab

  config.data.forEach((item, index) => {
    // console.warn('21312321', item?.itemname === '气表检查' && isQB)
    item.items.forEach((res) => {
      if (res.readonly !== true && hasKeyAndValue(newData, res.itemname)) {
        console.warn('>>>valueChange', item?.itemname, res.itemname, JSON.stringify(newData[res.itemname]))
        const incoming = newData[res.itemname]
        const rawKey = (incoming && typeof incoming === 'object' && incoming.rawKey) ? incoming.rawKey : res.itemname
        res.value = (incoming && typeof incoming === 'object' && 'value' in incoming) ? incoming.value : incoming
        // ⭐ 如果字段有options配置（如下拉选项），同步的值必须在options中存在才算同步成功
        // 避免AI识别出的无效值（如模糊识别的文本）写入表单选项之外
        if (Array.isArray(res?.options) && res.options.length > 0) {
          const valueInOptions = res.options.some(opt => opt.data === res.value)
          if (!valueInOptions) {
            console.warn(`>>>sync options mismatch: ${res.itemname} = "${res.value}" 不在options中，跳过`)
            return // 跳过该字段，不记入matchedFieldInfos，不触发valueChange
          }
        }
        // 记录同步成功的属性信息
        matchedFieldInfos.push({ rawKey, tabIndex: index, fieldName: res.itemname })
        // 判断是否是隐患。
        if (Array.isArray(res?.options) && res.options.length > 0) {
          const result = res.options.find(item => item.data === res.value)
          console.warn('111result', JSON.stringify(result))
          const aging = result?.aging ?? 0
          console.warn('>>>valueChange', aging)
          const dateTimeStr = dayjs()
            .add(Number(aging), 'day')
            .format('YYYY-MM-DD HH:mm:ss')
          console.warn('>>>valueChange', dateTimeStr)
          if (result && result?.isdefect) {
            res.f_is_defect = true
            res.level = result?.level
            res.f_process_mode = result?.processMode
            res.defectdes = result?.defectdes
            res.f_defect_paths = value
            res.f_handle_date = dateTimeStr
            res.f_aging = aging
            res.aging = aging
            const defectRec = {
              ...result,
              tabName: item?.itemname,
              itemName: res?.itemname,
              f_is_defect: true,
              level: result?.level,
              f_process_mode: result?.processMode,
              defectdes: result?.defectdes,
              f_defect_paths: value,
              f_aging: aging,
              f_handle_date: dateTimeStr,
            }
            defectRecords.push(defectRec)
            // 将隐患结构化数据写回 newData，方便 successFileList 统一判断隐患，也方便设备同步/回更
            newData[res.itemname] = {
              value: res.value,
              rawKey: incoming?.rawKey ?? res.itemname,
              ...defectRec,
            }
          }
        }
        if (item?.slotName === 'gasDevice') {
          // 调用AddDevice的syncDeviceData方法同步数据
          callSlotMethod('gasDevice', 'syncDeviceData', deviceTab[0]?.payload?.deviceId, newData, value)
          found = true
        }
        else {
          valueChange(res, index)
          found = true
        }
      }
    })
  })
  // 记录本次同步的数据项对应的属性信息（itemId -> SyncedFieldInfo[]），用于后续跳转定位
  // 使用rawKey+tabIndex组合去重，避免重复同步同一属性导致多次记录
  if (data?.id && matchedFieldInfos.length > 0) {
    const existing = syncedItemInfoMap.value[data.id] || []
    const existingKeys = new Set(existing.map(info => `${info.rawKey}_${info.tabIndex}`))
    const newInfos = matchedFieldInfos.filter(info => !existingKeys.has(`${info.rawKey}_${info.tabIndex}`))
    if (newInfos.length > 0) {
      syncedItemInfoMap.value[data.id] = [...existing, ...newInfos]
    }
  }

  // 若识别到隐患，则把隐患数据写回到 photoAiRecognition 的 properties 指定 key 的 aiData 中
  if (defectRecords.length > 0) {
    defectRecords.forEach((rec: any) => {
      try {
        const incoming = newData?.[rec?.itemName]
        const rawKey = incoming?.rawKey || rec?.itemName
        callSlotMethod('photoAiRecognition', 'patchPropertyAiData', data?.id, rawKey, rec)
      }
      catch (e) {
        console.warn('回更 photoAiRecognition properties.aiData 失败：', e)
      }
    })
  }

  // ⭐ 循环结束后判断
  if (!found) {
    showSuccessToast(`${data.name}数据项未找到`)
  }
  else {
    showSuccessToast(`${data.name}同步成功`)
  }
  await nextTick()
  showConfig.value = true
}

/**
 * AI识别跳转：点击已同步的数据属性时，跳转到对应的表单tab并定位到指定表单项
 * 流程：查找目标信息 -> 切换tab -> 滚动容器到顶部 -> 等待tab渲染 -> 滚动到具体字段
 */
function handleAiRecognitionJump(payload: { item: any, rawKey?: string }) {
  const { item, rawKey } = payload
  console.warn('handleAiRecognitionJump', JSON.stringify(payload))
  const itemId = item?.id
  if (!itemId || !syncedItemInfoMap.value[itemId]) {
    return
  }
  // 从同步信息映射中查找目标属性对应的tab索引和fieldName
  const infos = syncedItemInfoMap.value[itemId]
  const targetInfo = rawKey
    ? infos.find(info => info.rawKey === rawKey) // 点击了具体属性，定位到该属性
    : infos[0] // 未指定rawKey时跳转到第一个同步的属性

  console.warn('handleAiRecognitionJump targetInfo', JSON.stringify(targetInfo))
  if (!targetInfo) {
    return
  }
  // 切换tab到目标属性所在的tab（+1因为Vant Tabs索引从1开始）
  const targetTabIndex = targetInfo.tabIndex + 1
  activeTabIndex.value = targetTabIndex
  if (tabsRef.value) {
    (tabsRef.value as any).scrollTo(targetTabIndex)
  }
  // 等待tab切换渲染完成后，滚动容器到顶部，再定位到具体字段
  nextTick(() => {
    const tabsContainer = tabsContainerRef.value
    if (tabsContainer) {
      tabsContainer.scrollTo({ top: 0, behavior: 'smooth' })
    }
    // 延迟350ms等待tab面板渲染完成，再滚动到具体字段位置
    setTimeout(() => {
      scrollToSpecifiedField(targetInfo.fieldName)
    }, 350)
  })
}

/**
 * 在表单页面中滚动到指定名称的表单项并高亮
 * 直接在document范围内查找 data-field-name 匹配的字段元素
 * 使用scrollIntoView将字段滚动到可视区域居中位置，确保不被sticky header遮挡
 */
function scrollToSpecifiedField(fieldName: string) {
  console.warn('scrollToSpecifiedField', JSON.stringify(fieldName))
  const field = document.querySelector(`[data-field-name="${fieldName}"]`) as HTMLElement | null
  console.warn('scrollToSpecifiedField field', JSON.stringify(field))
  if (!field) {
    console.warn(`scrollToSpecifiedField 未找到 fieldName=${fieldName}`)
    return
  }
  // 使用scrollIntoView将字段滚动到可视区域并居中，浏览器会自动处理滚动容器和sticky元素的偏移
  field.scrollIntoView({ behavior: 'smooth', block: 'center' })
  // 添加高亮class，2秒后自动移除
  field.classList.add('highlight-field')
  setTimeout(() => field.classList.remove('highlight-field'), 2000)
}

// 清理逻辑
async function cleanupSecurityCertificate() {
  if (bubbleClickTimer.value !== null) {
    clearTimeout(bubbleClickTimer.value)
    bubbleClickTimer.value = null
  }
  document.removeEventListener('click', handleOutsideClick)
  floatingBubbleStatus.value = false
  stopStatusWatch?.()
  stopFloatingBubbleWatch?.()
  stopActiveTabWatch?.()
  stopTimer()
  safecheckStore.clearChangeTableId()
  resetLocalState()
  if (!isReadOnly.value && isRecording.value) {
    try {
      await stopRecordingSafely()
    }
    catch (error) {
      console.error('停止录音失败:', error)
    }
  }
}

// 是否当前离开目标是进入小工具
function isEnteringToolRoute(targetPath: string, targetName: string) {
  const isHome = String(targetName) === '返回首页' || targetPath === '/'
  return (toolRouteWhitelist.value.has(targetPath) || toolRouteWhitelist.value.has(String(targetName))) && !isHome
}

// keep-alive 缓存离开时：小工具不处理，否则按退出页面清空
onDeactivated(async () => {
  const targetPath = leavingTarget.value?.path || ''
  const targetName = (leavingTarget.value?.name as any) || ''
  console.log('>>>> onDeactivated', targetPath, targetName)
  if (isEnteringToolRoute(targetPath, targetName)) {
    console.log('进入小工具页面，跳过缓存离开清理')
    return
  }
  await cleanupSecurityCertificate()
})

// 组件卸载时移除事件监听
onBeforeUnmount(async () => {
  console.log('>>>> onBeforeUnmount')
  await cleanupSecurityCertificate()
})

// 离开路由前拦截：录音未保存提示
onBeforeRouteLeave((to, from, next) => {
  console.log('>>>> onBeforeRouteLeave')
  console.log('离开路由前拦截：录音未保存提示', isRecording.value)
  console.log('离开路由前拦截：是否只读', isReadOnly.value)
  console.log('离开路由前拦截：录音开关状态', recordSwitch.value)
  console.log('离开路由前拦截：是否正在保存并返回', isSavingAndReturning.value)

  // 如果正在保存并准备返回，直接放行，不进行拦截
  if (isSavingAndReturning.value) {
    console.log('正在保存并返回，跳过路由拦截')
    return next()
  }

  // 进入小工具页面：直接放行，不提示也不停止录音
  const targetPath = to?.path || ''
  const targetName = (to?.name as any) || ''
  leavingTarget.value = { path: targetPath, name: targetName }
  if (isEnteringToolRoute(targetPath, targetName)) {
    console.log('离开路由前拦截：进入小工具，直接放行')
    return next()
  }

  // 只有在非只读模式、录音开关开启且正在录音时才进行拦截
  if (!isReadOnly.value && recordSwitch.value && isRecording.value) {
    // 设置回退拦截 前端接管
    setAppBackIntercept(true)

    showConfirmDialog({
      title: '返回提示',
      message: '当前录音尚未保存，是否确定返回并停止录音？',
      confirmButtonText: '留在本页',
      cancelButtonText: '返回',
      closeOnPopstate: false,
    }).then(() => {
      // 确认：留在本页
      next(false)
    }).catch(async () => {
      // 取消：返回上一页（先尝试停止录音）
      try {
        await stopRecordingSafely()
        next()
      }
      catch (error) {
        next()
      }
    }).finally(() => {
      setAppBackIntercept(false)
    })
  }
  else {
    next()
  }
})

// 离开路由前需要进行的操作
function clear() {
  // 停止计时器
  stopTimer()
  // 清空换表ID(秦华)
  safecheckStore.clearChangeTableId()
}

provide('provideParent', {
  // 后续需要什么就加什么
  commonId: computed({
    get() {
      return commonId.value
    },
    set() {},
  }),
  valueChange: checkItemChange,
  dispatchDeptList: computed({
    get() {
      return dispatchDeptList.value
    },
    set() {},
  }),
  repairmanList: computed({
    get() {
      return repairmanList.value
    },
    set() {},
  }),
  safeConfig: computed({
    get() {
      return safeConfig.value
    },
    set() {},
  }),
  verificationCodeKey: computed({
    get() {
      return verificationCodeKey.value
    },
    set() {},
  }),
})
</script>

<template>
  <div v-if="!status" style="display: flex; justify-content: center; align-items: center; height: 100vh;">
    <VanLoading vertical size="50">
      努力加载中...
    </VanLoading>
  </div>
  <div v-if="status" class="security-certificate-container edit-mode">
    <div ref="tabsContainerRef" class="tabs-container">
      <!-- 标签页容器 -->
      <div class="tabs-wrapper">
        <VanTabs ref="tabsRef" v-model:active="activeTabIndex" :ellipsis="false" sticky class="custom-tabs" :offset-top="offsetTop">
          <!-- 自定义导航栏右侧的下拉菜单 -->
          <template #nav-right>
            <div class="custom-nav-right">
              <VanPopover
                v-model:show="tabStatus"
                class="securrity-pop"
                :actions="updatedActions"
                placement="bottom-end"
                @select="PageSwitch($event)"
              >
                <template #reference>
                  <VanButton :icon="popoverIcon" square icon-position="left" type="default" size="mini" class="custom-button" />
                </template>
                <template #action="{ action }">
                  <div class="custom-popover-action" :class="{ active: action.value === activeTabIndex }">
                    <span class="popover-action-text">{{ action.text }}</span>
                    <span v-if="action.errorCount > 0" class="error-badge">{{ action.errorCount }}</span>
                  </div>
                </template>
              </VanPopover>
            </div>
          </template>
          <VanTab>
            <template #title>
              <div class="tab-title">
                <span>基本信息</span>
                <span v-if="errorCounts.userInfo > 0" class="error-badge">{{ errorCounts.userInfo }}</span>
              </div>
            </template>
            <div class="x-form-group">
              <userInfo v-if="userDetail" :id="checkId" ref="userInfoRef" :user-detail="userDetail" :is-read-only="isReadOnly" @photo-quality-change="onPhotoQualityChange" />
            </div>
          </VanTab>
          <template v-for="(row, pIndex) in config.data" :key="row.itemname + pIndex">
            <VanTab>
              <template #title>
                <div class="tab-title">
                  <span>{{ row.itemname }}</span>
                  <span v-if="errorCounts.tabs[pIndex] > 0" class="error-badge">{{ errorCounts.tabs[pIndex] }}</span>
                </div>
              </template>

              <!-- 根据type字段渲染不同内容 -->
              <div v-if="row.type === 'slot'" class="x-form-group-item">
                <!-- 动态渲染自定义组件 -->
                <div>
                  <component
                    :is="slotComponentMap[row.slotName as string]"
                    v-if="row.slotName && slotComponentMap[row.slotName as string]"
                    :ref="(el) => { if (el) slotRefs[pIndex] = el }"
                    :config="row"
                    :row="row"
                    :user-detail="userDetail"
                    :model-value="componentInitData"
                    :is-view-mode="isReadOnly"
                    :extra-data="row?.extraData || {}"
                    :ai-synced-info-map="syncedItemInfoMap"
                    :blur-threshold="blurThreshold"
                    @value-change="(selVal, selObj, currentVal, payload) => { valueChange(currentVal, pIndex, payload) }"
                    @device-deleted="cleanupDeletedDeviceData"
                    @success-file-list="successFileList"
                    @ai-jump="handleAiRecognitionJump"
                    @photo-quality-change="onPhotoQualityChange"
                  />
                  <div v-else class="default-slot-container">
                    <h3>{{ row.itemname }}</h3>
                    <p>自定义插槽内容 - {{ row.slotName }}</p>
                  </div>
                </div>
              </div>
              <!-- 原有的checkItem类型渲染逻辑 -->
              <div v-if="showConfig && row.type !== 'slot'" class="x-form-group-item">
                <VanForm :ref="(el) => { if (el) formRefs[pIndex] = el as any }" @submit="onSubmit">
                  <VanCellGroup>
                    <SecurityFormItem
                      :row="row"
                      :user-detail="userDetail"
                      :safe-config="safeConfig"
                      :is-read-only="isReadOnly"
                      :checker-info="checkerInfo"
                      @value-change="(selVal, selObj, currentVal) => { valueChange(currentVal, pIndex) }"
                      @photo-quality-change="onPhotoQualityChange"
                    />
                  </VanCellGroup>
                </VanForm>
              </div>
            </VanTab>
          </template>
          <VanTab>
            <template #title>
              <div class="tab-title">
                <span>整体隐患</span>
                <span v-if="errorCounts.hiddenDangers > 0" class="error-badge">{{ errorCounts.hiddenDangers }}</span>
              </div>
            </template>
            <div class="x-form-group">
              <hiddenDangers ref="hiddenDangersRef" :defects="defectList" :custom-defects="customDefectsData" :is-view-mode="isReadOnly" @update-defects="onHiddenDangersUpdate" @photo-quality-change="onPhotoQualityChange" />
            </div>
          </VanTab>
          <VanTab>
            <template #title>
              <div class="tab-title">
                <span>拍照签名</span>
                <span v-if="errorCounts.photoSignature > 0" class="error-badge">{{ errorCounts.photoSignature }}</span>
              </div>
            </template>
            <div class="x-form-group">
              <component
                :is="safeConfig?.photoSignatureComponent ? photoSignatureComponentMap[safeConfig?.photoSignatureComponent] : photoSignatureComponentMap.photoSignature"
                ref="photoSignatureRef"
                :photo-notes="safeConfig?.checkPhotoPrompt"
                :is-view-mode="isReadOnly"
                :signature-form="signatureFormData"
                :signature-photo="signaturePhotoData"
                :signature-all-info="signatureAllInfo"
                :defect-list="defectList"
                :draft-type="draftType"
                @photo-quality-change="onPhotoQualityChange"
              />
              <!-- photoSignature ref="photoSignatureRef" :photo-notes="safeConfig?.checkPhotoPrompt" :is-view-mode="isReadOnly" :signature-form="signatureFormData" :signature-photo="signaturePhotoData" / -->
            </div>
          </VanTab>
        </VanTabs>
      </div>
    </div>
    <!-- 新增底部按钮区域 - 仅在修改模式下显示 -->
    <div v-if="!isReadOnly" class="footer-buttons">
      <VanButton icon="passed" type="primary" class="submit-btn" @click="onSubmit">
        上传
      </VanButton>
      <VanButton v-if="(!hideSaveButton) && (!safeCheckPaperId)" icon="bookmark" type="warning" class="save-btn" @click="saveDraftData">
        保存
      </VanButton>
      <!--      测试 -->
      <!--      <VanButton icon="passed" type="primary" class="submit-btn" @click="lookUpload"> -->
      <!--        看看上传 -->
      <!--      </VanButton> -->
      <div class="slide-buttons">
        <VanButton type="default" plain icon="arrow-left" size="small" @click="slideLeft" />
        <VanButton type="default" plain icon="arrow" size="small" @click="slideRight" />
      </div>
    </div>
    <div v-if="isReadOnly" class="footer-buttons">
      <div class="slide-buttons" style="width: 100%">
        <VanButton style="width: 50%" type="default" plain size="small" @click="slideLeft">
          上一页
        </VanButton>
        <VanButton style="width: 50%" type="default" plain size="small" @click="slideRight">
          下一页
        </VanButton>
      </div>
    </div>

    <!-- 悬浮气泡（小工具） -->
    <FloatingBubble
      v-model:offset="offset"
      icon="bag-o"
      axis="xy"
      magnetic="x"
      @click="onFloatingBubble"
    />

    <!-- 自定义悬浮菜单 -->
    <teleport to="body">
      <div v-if="floatingBubbleStatus" class="bubble-menu-container" :style="menuPosition">
        <div class="bubble-menu">
          <div
            v-for="(action, index) in floatingBubbleActions"
            :key="index"
            class="bubble-menu-item"
            @click="floatingBubbleSwitch(action)"
          >
            <VanIcon :name="action.icon" class="bubble-menu-icon" />
            <span :style="{ whiteSpace: 'nowrap' }">{{ action.title }}</span>
          </div>
        </div>
      </div>
    </teleport>

    <!-- 悬浮气泡（计时器） -->
    <FloatingBubble
      v-if="safeConfig?.isTimer && !isReadOnly"
      v-model:offset="offset2"
      axis="xy"
      magnetic="x"
    >
      <template #default>
        <div class="floating-timer">
          <div class="timer-text">
            {{ timerDisplay }}
          </div>
        </div>
      </template>
    </FloatingBubble>
  </div>
</template>

<style scoped lang="less">
.security-certificate-container {
  --van-field-label-width: 7em;
  height: calc(100vh - var(--system-header-height));
  display: flex;
  flex-direction: column;
}

.tabs-container {
  flex: 1;
  overflow-y: auto;
  overflow-x: hidden;
  position: relative; /* 使 fixed-popover-container 相对于 tabs-container 定位 */
  padding-bottom: 80px; /* 为底部按钮留出空间 */
}

/* 查看模式下不需要为底部按钮留空间 */
.security-certificate-container:not(.edit-mode) .tabs-container {
  padding-bottom: 10px;
}
.tabs-wrapper {
  width: 100%; /* 确保 van-tabs 占据整个宽度 */
  background-color: #f7f8fa;
  padding-bottom: 10px;
}

.x-form-group {
  margin: 0;
  background-color: #fff;
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
  overflow: hidden;
}

:deep(.custom-tabs > .van-tabs__content > .van-tab__panel) {
  padding: 4px 10px;
}

.custom-tabs {
  :deep(.van-tabs__nav) {
    background-color: #fff;
    border-bottom: 1px solid #f0f0f0;
  }

  :deep(.van-tab) {
    padding-right: 10px; /* 为标题右侧的徽标留出更多空间 */
  }

  :deep(.van-tabs__line) {
    background-color: #1989fa;
    height: 3px;
  }
}

/* 自定义导航栏右侧样式 */
.custom-nav-right {
  display: flex;
  align-items: center;
  height: 100%;
}

.x-form-title {
  font-size: 16px;
  color: #333;
  font-weight: bold;
  background-color: #fff;
  padding: 15px 20px;
  margin: 0;
  border-bottom: 1px solid #f0f0f0;
  display: flex;
  align-items: center;
  gap: 8px;
}

/* 移除原来的 fixed-popover-container 样式，因为已经集成到标签页导航中 */
.securrity-pop {
  border-radius: 8px;
}
.footer-buttons {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 16px;
  position: fixed; /* 固定位置 */
  bottom: 0; /* 距离底部0像素 */
  left: 0;
  right: 0;
  width: 100%; /* 宽度占满整个页面 */
  background-color: white; /* 背景颜色，防止内容被遮挡 */
  box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1); /* 添加阴影，提升视觉效果 */
  z-index: 1000;

  .submit-btn {
    flex: 1;
    margin-right: 8px;
    height: 44px;
    border-radius: 8px;
    font-size: 16px;
    font-weight: 500;
  }

  .save-btn {
    flex: 1;
    margin-right: 12px;
    height: 44px;
    border-radius: 8px;
    font-size: 16px;
    font-weight: 500;
  }

  .slide-buttons {
    display: flex;
    gap: 8px;

    .van-button {
      height: 44px;
      width: 44px;
      border-radius: 8px;
      font-size: 14px;
      padding: 0;
      min-width: 44px;
    }
  }
}

.custom-button {
  z-index: 5;
  border: none !important;
  box-shadow: none !important;
  pointer-events: none; // 禁用点击反馈
  color: #666;
  border-radius: 0 !important;
  padding: 6px 12px;
  font-size: 12px;

  :deep(.van-icon) {
    vertical-align: middle;
    margin-right: 6px;
    margin-bottom: 6px;
    color: #666;
  }
}

.error-badge {
  display: inline-block;
  background-color: #ff4757;
  color: #fff;
  border-radius: 50%;
  font-size: 12px;
  height: 16px;
  width: 16px;
  line-height: 16px;
  text-align: center;
  margin-left: 4px;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2); /* 添加阴影增强可见性 */
}

.custom-popover-action {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  padding: 8px 0;
  border-radius: 8px;
  transition: background-color 0.2s ease;

  &:hover {
    background-color: #f7f8fa;
  }

  .popover-action-text {
    flex: 1;
    text-align: left;
    margin-right: 12px;
    font-size: 14px;
    font-weight: 500;
    color: #323233;
    line-height: 1.4;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 200px;
  }

  &.active {
    .popover-action-text {
      color: #1989fa;
      font-weight: 600;
    }
  }

  .error-badge {
    font-size: 12px;
    height: 16px;
    width: 16px;
    line-height: 16px;
    flex-shrink: 0; /* 防止徽标被挤压变形 */
    font-weight: 600;
    box-shadow: 0 2px 4px rgba(255, 0, 0, 0.3);
    animation: pulse 2s infinite;
  }
}

/* 添加脉冲动画 */
@keyframes pulse {
  0% {
    transform: scale(1);
  }
  50% {
    transform: scale(1.1);
  }
  100% {
    transform: scale(1);
  }
}

:deep(.van-popover__action) {
  height: auto;
}

:deep(.van-popover__action-text) {
  justify-content: left !important;
}

.tab-title {
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
  padding: 0 3px; /* 添加一些内边距 */
  min-height: 20px; /* 确保标题有足够高度 */

  .error-badge {
    position: absolute;
    top: -1.5px; /* 微调顶部位置 */
    right: -10px; /* 微调右侧位置 */
    margin-left: 0;
    z-index: 10; /* 确保徽标始终显示在顶层 */
    font-size: 10px; /* 右上角位置徽标稍微小一点 */
    height: 14px;
    width: 14px;
    line-height: 14px;
  }
}

/* 气泡样式 */
:deep(.van-floating-bubble) {
  --van-floating-bubble-size: 55px;
  --van-floating-bubble-background: #1989fa;
  --van-floating-bubble-color: #fff;
  z-index: 999;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
}

:deep(.van-floating-bubble__icon) {
  font-size: 26px;
}

/* 气泡菜单样式 */
.bubble-menu-container {
  position: fixed;
  z-index: 2000;
}

.bubble-menu {
  background-color: white;
  border-radius: 8px;
  overflow: hidden;
  min-width: 140px;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.12);
}

.bubble-menu-item {
  display: flex;
  align-items: center;
  padding: 12px 16px;
  font-size: 14px;
  line-height: 20px;
  color: #323233;
  cursor: pointer;

  &:active {
    background-color: #f2f3f5;
  }

  &:not(:last-child) {
    border-bottom: 1px solid #ebedf0;
  }
}

.bubble-menu-icon {
  margin-right: 8px;
  font-size: 18px;
  color: #1989fa;
}

.default-slot-container {
  min-height: 120px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  background-color: #f7f8fa;
  border: 1px dashed #dcdee0;
  border-radius: 6px;
  padding: 20px;

  h3 {
    color: #1989fa;
    margin-bottom: 8px;
  }
}

/* 表单字段样式调整 */
:deep(.van-field) {
  //padding: 16px 20px;
  //background-color: #fff;
  border-bottom: 1px solid #f0f0f0;

  &:last-child {
    border-bottom: none;
  }

  .van-field__label {
    color: #333;
    font-size: 14px;
    font-weight: 500;
    display: flex;
    align-items: center;
    gap: 8px;
  }

  .van-field__value {
    color: #666;
    font-size: 14px;
  }
  .van-field__control {
    color: #333;
    font-size: 14px;
  }

  .van-field__placeholder {
    color: #ff4757;
    font-size: 14px;
  }
}

/* 必填字段样式 */
:deep(.van-field--required) {
  .van-field__label::before {
    content: '*';
    color: #ff4757;
    margin-right: 4px;
  }
}

/* 选择器样式 */
:deep(.van-cell) {
  //padding: 16px 20px;
  //background-color: #fff;
  border-bottom: 1px solid #f0f0f0;

  &:last-child {
    border-bottom: none;
  }

  .van-cell__title {
    color: #333;
    font-size: 14px;
    font-weight: 500;
    display: flex;
    align-items: center;
    gap: 8px;
  }

  .van-cell__value {
    color: #666;
    font-size: 14px;
  }
}
:deep(.van-cell) {
  border-bottom: none;
}

/* 表单组样式 */
.x-form-group-item {
  background-color: #fff;
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
  margin: 0;
  overflow: hidden;
}
/* VanPopover 弹出内容区域样式 */

:deep(.van-popover__content) {
  padding: 12px;
  background: rgba(254, 254, 254);
  border-radius: 8px;
  box-shadow: none;
}
:deep(.van-popover__arrow) {
  border-color: #fff !important;
}

/* 悬浮气泡计时器样式 */
.floating-timer {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 8px;
  .timer-icon {
    font-size: 20px;
    margin-bottom: 2px;
  }
  .timer-text {
    font-size: 12px;
    font-weight: 600;
    color: #f0f0f0;
    font-family: monospace;
  }
}

/* 跳转高亮动画：点击AI识别同步数据跳转到表单项时，短暂闪烁蓝色背景2秒
   使用!important覆盖Vant默认样式，确保高亮效果可见 */
:deep(.highlight-field) {
  transition: background-color 0.5s ease;
  background-color: #e6f7ff !important; /* 淡蓝色背景 */

  .van-field__label {
    color: #1989fa !important; /* 蓝色label文字 */
    font-weight: 700 !important;
  }
}
</style>
