<template>
  <div v-if="template && template.config" class="template-readonly">
    <!-- 遍历模板配置的各个区域 -->
    <div
      v-for="(section, sectionKey) in template.config"
      :key="sectionKey"
      class="template-section"
      v-show="shouldRenderSection(section)"
    >
      <!-- 区域标题 -->
      <h5 v-if="section.title" class="section-title">{{ section.title }}</h5>

      <!-- 普通字段区域 -->
      <template v-if="section.fields && Array.isArray(section.fields)">
        <table class="info-table">
          <template v-for="field in getVisibleFields(section)" :key="field.key || field.element_id">
            <tr>
              <td class="label">{{ field.label || field.name || '-' }}</td>
              <td class="value">
              <template v-if="getFieldValue(field)">
                <!-- 文本类型 -->
                <span v-if="field.element_type === 'text' || field.element_type === 'textarea'">
                  {{ getFieldValue(field) }}
                </span>
                <!-- 数字类型 -->
                <span v-else-if="field.element_type === 'number'">
                  {{ formatNumber(getFieldValue(field)) }}
                </span>
                <!-- 日期类型 -->
                <span v-else-if="field.element_type === 'date'">
                  {{ formatDate(getFieldValue(field)) }}
                </span>
                <!-- 勾选清单类型 -->
                <div v-else-if="field.element_type === 'checklist'" class="readonly-checklist">
                <!-- 有配置 options：按"勾选/备注"过滤展示 -->
                <template v-if="Array.isArray(getChecklistOptions(field)) && getChecklistOptions(field).length">
                  <div
                    v-for="(opt, idx) in (getChecklistOptions(field) || [])"
                    :key="opt.value || idx"
                    v-show="isChecklistChecked(getFieldValue(field), opt.value) || getChecklistRemark(field, opt.value)"
                    class="checklist-option-item"
                  >
                    <div class="checklist-option-wrapper">
                      <el-checkbox
                        :model-value="isChecklistChecked(getFieldValue(field), opt.value)"
                        disabled
                        class="checklist-checkbox"
                      >
                          {{ `${idx + 1}、${opt.label}` }}
                      </el-checkbox>
                        <span
                          v-if="getChecklistRemark(field, opt.value)"
                          class="checklist-remark-text"
                        >
                          {{ getChecklistRemark(field, opt.value) }}
                      </span>
                    </div>
                  </div>
                </template>

                <!-- 没有 options：兜底直接展示已选值（避免"空白"） -->
                <template v-else>
                  <div v-if="getChecklistFallbackItems(field).length" class="checklist-fallback">
                    <div
                      v-for="(v, idx) in getChecklistFallbackItems(field)"
                      :key="`${v}_${idx}`"
                      class="checklist-fallback-item"
                    >
                      {{ `${idx + 1}、${v}` }}
                    </div>
                  </div>
                  <span v-else class="empty-value">-</span>
                </template>
              </div>
              <!-- 会议纪要类型 -->
              <div v-else-if="field.element_type === 'meeting_minutes'">
                <MeetingMinutesField
                  :model-value="getFieldValue(field)"
                  readonly
                />
              </div>
              <!-- 明细表格类型（卡片渲染） -->
              <div v-else-if="field.element_type === 'detail_table'" class="detail-table-readonly">
                <div v-if="Array.isArray(getFieldValue(field)) && getFieldValue(field).length" class="detail-table-cards">
                  <div
                    v-for="(row, rowIdx) in getDetailTableData(field)"
                    :key="`row_${rowIdx}`"
                    class="detail-table-card"
                >
                    <div class="detail-table-card__header">
                      <span class="detail-table-card__index">第 {{ rowIdx + 1 }} 条</span>
                    </div>
                    <div class="detail-table-card__body">
                  <template v-if="getDetailTableFields(field).length">
                        <div
                      v-for="col in getDetailTableFields(field)"
                      :key="col.field_key"
                          v-show="!isEmptyDetailTableValue(row[col.field_key])"
                          class="detail-table-card__field"
                    >
                          <span class="detail-table-card__label">{{ col.field_name }}</span>
                          <span class="detail-table-card__value">
                        <span v-if="col.field_type === 'department'">
                          {{ getDepartmentName(row[col.field_key]) }}
                        </span>
                        <span v-else-if="col.field_type === 'user'">
                          {{ getUserName(row[col.field_key]) }}
                        </span>
                        <span v-else>
                          {{ row[col.field_key] ?? '-' }}
                        </span>
                          </span>
                        </div>
                  </template>
                  <template v-else>
                        <div
                      v-for="k in getAutoKeysForDetailTable(field)"
                      :key="k"
                          v-show="!isEmptyDetailTableValue(row[k])"
                          class="detail-table-card__field"
                        >
                          <span class="detail-table-card__label">{{ k }}</span>
                          <span class="detail-table-card__value">{{ row[k] ?? '-' }}</span>
                        </div>
                  </template>
                    </div>
                  </div>
                </div>
                <span v-else class="empty-value">-</span>
              </div>
              <!-- 流程类型 -->
              <div v-else-if="field.element_type === 'oa_custom_model'" class="approval-flow-display">
                <div v-if="getFlowItems(field).length" class="flow-info">
                  <template v-for="(it, idx) in getFlowItems(field)" :key="`${it.id || it.display_name}_${idx}`">
                    <span v-if="idx > 0">、</span>
                    <el-link
                      v-if="it.id"
                      type="primary"
                      :underline="false"
                      class="flow-info-clickable"
                      @click.prevent="openFlowDrawer(it.id, it.custom_model_id)"
                    >
                      {{ it.display_name || `流程ID: ${it.id}` }}
                    </el-link>
                    <span v-else class="flow-info-clickable">{{ it.display_name || '-' }}</span>
                  </template>
                </div>
                <span v-else class="muted">-</span>
              </div>
              <!-- 附件类型 -->
              <div v-else-if="field.element_type === 'file' || field.element_type === 'attachment'" class="attachment-display">
                <!-- 调试信息：显示字段信息和原始值 -->
                <div v-if="config.isDevelopment" style="font-size: 11px; color: #909399; margin-bottom: 4px; padding: 4px; background: #f5f7fa; border-radius: 4px;">
                  <div><strong>🔍 调试信息 [attachment/file字段]</strong></div>
                  <div>字段ID: {{ field.element_id || field.id || 'N/A' }}</div>
                  <div>字段Key: {{ field.key || field.field_key || 'N/A' }}</div>
                  <div>字段类型: {{ field.element_type }}</div>
                  <div>原始值类型: {{ typeof getFieldValue(field) }}</div>
                  <div>原始值: {{ JSON.stringify(getFieldValue(field)) }}</div>
                  <div>处理后的文件项数: {{ getFileItems(field).length }}</div>
                </div>
                <div v-if="getFileItems(field).length" class="file-list">
                  <template v-for="(f, idx) in getFileItems(field)" :key="`${f.url || f.name}_${idx}`">
                    <!-- 调试信息：显示每个文件项的详细信息 -->
                    <div v-if="config.isDevelopment" style="font-size: 10px; color: #67c23a; margin-bottom: 2px;">
                      [文件{{ idx + 1 }}] name: {{ f.name }}, url: {{ f.url || '无URL' }}
                    </div>
                    <el-link
                      v-if="f.url"
                      :href="f.url"
                      target="_blank"
                      type="primary"
                      :underline="false"
                      class="file-link"
                      @click="handleFileClick(f)"
                    >
                      {{ f.name || `附件${idx + 1}` }}
                    </el-link>
                    <el-link
                      v-else
                      type="primary"
                      :underline="false"
                      class="file-link file-link--no-url"
                      @click="handleFileClick(f)"
                    >
                      {{ f.name || `附件${idx + 1}` }}
                    </el-link>
                    <span v-if="idx < getFileItems(field).length - 1" class="file-separator">，</span>
                  </template>
                  </div>
                <span v-else class="muted">-</span>
              </div>
              <!-- 默认显示 -->
              <span v-else>
                {{ formatDefaultValue(getFieldValue(field)) }}
              </span>
            </template>
            <span v-else class="empty-value">-</span>
              </td>
            </tr>

            <!-- 会议纪要：若唯一附件为 PDF，则在本行后追加一个通栏行展示 PDF（尽量拉宽） -->
            <tr
              v-if="field.element_type === 'meeting_minutes' && getMeetingMinutesPdfUrl(field)"
              class="meeting-minutes-pdf-row"
            >
              <td class="value meeting-minutes-pdf-td" colspan="2">
                <div
                  class="print-file-item print-pdf meeting-minutes-pdf"
                  :data-pdf-id="`planned_meeting_minutes_${field.element_id || field.key}`"
                  :data-pdf-url="getMeetingMinutesPdfUrl(field)"
                >
                  <div class="pdf-pages-container">
                    <div class="pdf-loading">PDF 加载中...</div>
                  </div>
                </div>
              </td>
            </tr>
          </template>
        </table>
      </template>

      <!-- 支付轮次区域 -->
      <template v-else-if="section.rounds && Array.isArray(section.rounds)">
        <div
          v-for="(round, roundIndex) in section.rounds"
          :key="roundIndex"
          class="payment-round"
          v-show="getVisibleRoundFields(round).length > 0"
        >
          <h6 class="round-title">第 {{ round.round }} 轮支付</h6>
          <table class="info-table">
            <tr
            v-for="field in getVisibleRoundFields(round)"
            :key="field.key || field.element_id"
          >
              <td class="label">{{ field.label || field.name || '-' }}</td>
              <td class="value">
              <template v-if="getFieldValue(field)">
                <span v-if="field.element_type === 'text' || field.element_type === 'textarea'">
                  {{ getFieldValue(field) }}
                </span>
                <span v-else-if="field.element_type === 'number'">
                  {{ formatNumber(getFieldValue(field)) }}
                </span>
                <span v-else-if="field.element_type === 'date'">
                  {{ formatDate(getFieldValue(field)) }}
                </span>
                <span v-else>
                  {{ formatDefaultValue(getFieldValue(field)) }}
                </span>
              </template>
              <span v-else class="empty-value">-</span>
              </td>
            </tr>
          </table>
        </div>
      </template>
    </div>
  </div>
  <div v-else class="no-template">
    <span class="empty-value">暂无模板配置</span>
  </div>

  <!-- oa_custom_model：右侧抽屉 + iframe 打开 OA 查看页 -->
  <el-drawer
    v-model="flowDrawerVisible"
    title="流程查看"
    direction="rtl"
    size="72%"
    :modal="true"
    :close-on-click-modal="false"
    :close-on-press-escape="true"
    :show-close="true"
    class="flow-drawer"
    @close="closeFlowDrawer"
  >
    <div class="drawer-content" v-loading="flowIframeLoading">
      <iframe
        v-if="flowIframeUrl"
        ref="flowIframeRef"
        :src="flowIframeUrl"
        class="oa-flow-iframe"
        frameborder="0"
        scrolling="auto"
        @load="handleFlowIframeLoad"
        @error="handleFlowIframeError"
      ></iframe>
      <div v-else class="empty-value">正在准备加载流程页面...</div>
    </div>
  </el-drawer>
</template>

<script setup>
import { computed, onMounted, ref, watch } from 'vue'
import MeetingMinutesField from './MeetingMinutesField.vue'
import { departmentAPI, detailTableFieldAPI, userAPI } from '@/utils/api'
import { plannedExpenditureAPI, oaFlowAPI, templateElementAPI } from '@/utils/api'
import { getToken } from '@/utils/auth'
import { ElMessage } from 'element-plus'
import config from '@/config'

const props = defineProps({
  template: {
    type: Object,
    default: null
  },
  elementValues: {
    type: Object,
    default: () => ({})
  },
  flowBindings: {
    type: Array,
    default: () => ([])
  },
  hideEmpty: {
    type: Boolean,
    default: false
  }
})

const emit = defineEmits(['collect-flow-id'])

/**
 * 兼容两种 elementValues 结构：
 * 1) “值对象”模式（打印页 PaymentController 等）：{ [elementId]: { value, field_key, ... } }
 * 2) “字段键值”模式（PlannedExpenditureController）：{ [field_key]: any }，例如 { element_123: 'xxx' }
 */
const getElementValueRawByKey = (key) => {
  if (!key || !props.elementValues) return undefined
  // 允许 key 为数字/字符串
  return Object.prototype.hasOwnProperty.call(props.elementValues, key) ? props.elementValues[key] : undefined
}

// 获取字段值
const getFieldValue = (field) => {
  if (!field || !props.elementValues) {
    if (config.isDevelopment && (field?.element_type === 'file' || field?.element_type === 'attachment')) {
      console.log('[getFieldValue] 字段或elementValues为空', { field, elementValues: props.elementValues })
    }
    return null
  }
  
  // 1) 优先用 field_key 直接命中（兼容 element_values 为 { field_key: value } 的后端返回）
  const fieldKey = field.key || field.field_key
  if (fieldKey) {
    const direct = getElementValueRawByKey(fieldKey)
    if (direct !== undefined) {
      if (config.isDevelopment && (field.element_type === 'file' || field.element_type === 'attachment')) {
        console.log('[getFieldValue] 通过fieldKey找到值', { fieldKey, direct, fieldType: typeof direct, isObject: typeof direct === 'object' })
      }
      // 可能是对象模式，也可能是原始值
      if (direct && typeof direct === 'object' && direct.value !== undefined) return direct.value
      return direct
    }
  }

  // 2) 使用 element_id（兼容 element_values 为 { [elementId]: { value } } 的后端返回）
  const elementId = field.element_id
  if (elementId) {
    const ev = getElementValueRawByKey(elementId)
    if (ev !== undefined) {
      if (config.isDevelopment && (field.element_type === 'file' || field.element_type === 'attachment')) {
        console.log('[getFieldValue] 通过elementId找到值', { elementId, ev, fieldType: typeof ev, isObject: typeof ev === 'object' })
      }
      if (ev && typeof ev === 'object' && ev.value !== undefined) return ev.value
      // 某些场景 elementId 也可能直接对应原始值
      return ev
    }

    // 3) 兜底：field_key 可能是 element_{id}
    const fallbackKey = `element_${elementId}`
    const fallback = getElementValueRawByKey(fallbackKey)
    if (fallback !== undefined) {
      if (config.isDevelopment && (field.element_type === 'file' || field.element_type === 'attachment')) {
        console.log('[getFieldValue] 通过fallbackKey找到值', { fallbackKey, fallback, fieldType: typeof fallback })
      }
      if (fallback && typeof fallback === 'object' && fallback.value !== undefined) return fallback.value
      return fallback
    }
  }
  
  // 4) 兼容：elementValues 为"值对象"模式但未按 elementId 索引（遍历查 field_key）
  if (fieldKey) {
    for (const ev of Object.values(props.elementValues)) {
      if (!ev || typeof ev !== 'object') continue
      if (ev.field_key === fieldKey) {
        if (config.isDevelopment && (field.element_type === 'file' || field.element_type === 'attachment')) {
          console.log('[getFieldValue] 通过遍历找到值', { fieldKey, ev, value: ev.value })
        }
        return ev.value !== undefined ? ev.value : null
      }
    }
  }
  
  if (config.isDevelopment && (field.element_type === 'file' || field.element_type === 'attachment')) {
    console.warn('[getFieldValue] 未找到字段值', {
      fieldKey: field.key || field.field_key,
      elementId: field.element_id,
      field,
      elementValuesKeys: Object.keys(props.elementValues || {}),
      elementValuesSample: JSON.stringify(props.elementValues).substring(0, 500)
    })
  }
  
  return null
}

// 会议纪要：识别唯一附件是否为 PDF，并返回 URL（字段值可能是对象或 JSON 字符串）
const getMeetingMinutesPdfUrl = (field) => {
  if (!field || field.element_type !== 'meeting_minutes') return null
  const rawVal = getFieldValue(field)
  if (rawVal === null || rawVal === undefined || rawVal === '') return null

  let v = rawVal
  if (typeof v === 'string') {
    try {
      const parsed = JSON.parse(v)
      if (parsed && typeof parsed === 'object') v = parsed
    } catch {
      // ignore
    }
  }

  if (!v || typeof v !== 'object') return null

  const url = (v.file_url || v.url || '').toString().trim()
  if (!url) return null

  const name = (v.file_name || v.name || '').toString().trim()
  const extFromName = name.includes('.') ? name.split('.').pop() : ''
  const extFromUrl = url.split('?')[0].split('#')[0].split('.').pop()
  const ext = String(extFromName || extFromUrl || '').toLowerCase()

  return ext === 'pdf' ? url : null
}

// 检查勾选清单是否选中
const isChecklistChecked = (value, optionValue) => {
  if (!value) return false
  const arr = Array.isArray(value) ? value : (value ? [value] : [])
  const norm = (v) => (v === null || v === undefined) ? '' : String(v)
  const target = norm(optionValue)
  return arr.map(norm).includes(target)
}

// 获取勾选清单的备注
const getChecklistRemark = (field, optionValue) => {
  if (!field || !optionValue) return null
  
  const elementId = field.element_id
  if (!elementId) return null
  
  // 查找备注字段：element_{id}_remark_{value}
  const remarkKey = `element_${elementId}_remark_${optionValue}`
  // 1) 直接命中（兼容 element_values 为 { field_key: value }）
  const direct = getElementValueRawByKey(remarkKey)
  if (direct !== undefined) {
    if (direct && typeof direct === 'object' && direct.value !== undefined) return direct.value || null
    return direct || null
  }
  // 2) 遍历命中（兼容值对象模式）
  for (const ev of Object.values(props.elementValues)) {
    if (!ev || typeof ev !== 'object') continue
    if (ev.field_key === remarkKey) {
      return ev.value || null
    }
  }
  
  return null
}

const hasAnyChecklistContent = (field) => {
  const value = getFieldValue(field)
  const hasChecked = Array.isArray(value) ? value.length > 0 : !!value
  if (hasChecked) return true
  const opts = field?.options || []
  return opts.some(opt => !!getChecklistRemark(field, opt.value))
}

const shouldShowChecklistOption = (field, optionValue) => {
  return isChecklistChecked(getFieldValue(field), optionValue) || !!getChecklistRemark(field, optionValue)
}

const getChecklistFallbackItems = (field) => {
  const v = getFieldValue(field)
  const arr = Array.isArray(v) ? v : (v ? [v] : [])
  return arr
    .map(x => (x === null || x === undefined) ? '' : String(x))
    .map(s => s.trim())
    .filter(Boolean)
}

const shouldRenderField = (field) => {
  if (!props.hideEmpty) return true
  if (!field) return false
  if (field.element_type === 'checklist') {
    // checklist：即使没有勾选/备注，也要展示全部选项（用户要求）
    const opts = field?.options || []
    if (Array.isArray(opts) && opts.length) return true
    return hasAnyChecklistContent(field)
  }
  const v = getFieldValue(field)
  if (v === null || v === undefined) return false
  if (typeof v === 'string') return v.trim() !== ''
  if (Array.isArray(v)) return v.length > 0
  if (typeof v === 'object') return Object.keys(v).length > 0
  return true
}

const getVisibleFields = (section) => {
  const fields = Array.isArray(section?.fields) ? section.fields : []
  return fields.filter(shouldRenderField)
}

const getVisibleRoundFields = (round) => {
  const fields = Array.isArray(round?.fields) ? round.fields : []
  return fields.filter(shouldRenderField)
}

const shouldRenderSection = (section) => {
  if (!props.hideEmpty) return true
  if (Array.isArray(section?.fields)) return getVisibleFields(section).length > 0
  if (Array.isArray(section?.rounds)) {
    return section.rounds.some(r => getVisibleRoundFields(r).length > 0)
  }
  return true
}

// 格式化数字
const formatNumber = (num) => {
  if (num === null || num === undefined || num === '') return '-'
  return Number(num).toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 })
}

// 格式化日期
const formatDate = (date) => {
  if (!date) return '-'
  if (typeof date === 'string') {
    const d = new Date(date)
    if (isNaN(d.getTime())) return date
    return d.toLocaleDateString('zh-CN')
  }
  return date
}

// oa_custom_model：可能多条，优先用后端 flow_bindings（display_name 更可靠）
const normalizeFlowId = (v) => {
  if (v === null || v === undefined || v === '') return null
  const s = String(v).trim()
  if (!s) return null
  if (!s.match(/^\d+$/)) return null
  return Number(s)
}

const extractFlowIds = (value) => {
  if (value === null || value === undefined || value === '') return []
  if (Array.isArray(value)) {
    return value.map(normalizeFlowId).filter(Boolean)
  }
  if (typeof value === 'string') {
    // 兼容 “1,2,3” / “1，2，3”
    return value
      .split(/[,，]/g)
      .map(s => normalizeFlowId(s))
      .filter(Boolean)
  }
  if (typeof value === 'number') {
    return [normalizeFlowId(value)].filter(Boolean)
  }
  if (typeof value === 'object') {
    const id = normalizeFlowId(value.instance_id ?? value.id)
    return id ? [id] : []
  }
  return []
}

const getFlowItems = (field) => {
  const fieldKey = field?.key || field?.field_key
  const elementId = field?.element_id ? Number(field.element_id) : null
  const bindings = Array.isArray(props.flowBindings) ? props.flowBindings : []

  let flowIds = []

  // 1) 优先按 element_id / field_key 精确匹配（可能多条）
  const direct = bindings.filter(b => {
    if (!b) return false
    if (elementId && b.element_id && Number(b.element_id) === elementId) return true
    if (fieldKey && b.field_key === fieldKey) return true
    return false
  })
  if (direct.length) {
    flowIds = direct.map(b => b.flow_instance_id).filter(Boolean)
  } else {
    // 2) 再从字段值里解析 ID
    flowIds = extractFlowIds(getFieldValue(field))
  }
  
  if (!flowIds.length) return []
  
  // 批量加载流程详情（如果还没有加载）
  loadFlowDetailsBatch(flowIds)
  
  // 构建返回项
  const items = flowIds.map(id => {
    const hit = bindings.find(b => Number(b?.flow_instance_id) === Number(id))
    const detail = flowDetailsMap.value[id]
    
    // 构建显示文本：{流程编号} - {flow_title}（去掉状态）
    let displayText = ''
    if (detail) {
      const parts = []
      if (detail.no) parts.push(detail.no)
      if (detail.title) {
        if (parts.length) parts.push(' - ')
        parts.push(detail.title)
      }
      displayText = parts.length ? parts.join('') : `流程ID: ${id}`
    } else {
      // 如果没有详情，使用流程ID
      displayText = `流程ID: ${id}`
    }
    
    // 向父组件emit流程ID（用于收集打印模版）
    if (id) {
      emit('collect-flow-id', id)
    }
    
    return {
      id,
      custom_model_id: hit?.flow_custom_model_id ?? null,
      display_name: displayText,
      detail // 保存详情以便后续使用
    }
  })
  
  return items
}

const loadFlowDetailsBatch = async (ids) => {
  const need = (ids || []).filter(Boolean).filter(id => !flowDetailsMap.value[id])
  if (!need.length) return flowDetailsMap.value
  try {
    const res = await plannedExpenditureAPI.getOaFlowDetails(need)
    if (res.code === 0 && res.data) {
      // 合并到缓存中
      Object.keys(res.data).forEach(id => {
        if (res.data[id]) {
          flowDetailsMap.value[id] = res.data[id]
        }
      })
    }
  } catch {
    // ignore
  }
  return flowDetailsMap.value
}

// 获取流程状态文本（中文）
const getFlowStatusText = (status) => {
  if (!status) return '未知'
  const statusStr = String(status).toLowerCase()
  const statusMap = {
    'pending': '待审批',
    'processing': '审批中',
    'approved': '已通过',
    'rejected': '已拒绝',
    'cancelled': '已取消',
    'draft': '草稿',
    'completed': '已完成'
  }
  return statusMap[statusStr] || String(status)
}

// flow 抽屉
const flowDrawerVisible = ref(false)
const flowIframeUrl = ref('')
const flowIframeLoading = ref(false)
const flowIframeRef = ref(null)
let flowIframeLoadTimeout = null

const closeFlowDrawer = () => {
  flowDrawerVisible.value = false
  setTimeout(() => {
    flowIframeUrl.value = ''
    flowIframeLoading.value = false
    if (flowIframeRef.value) {
      flowIframeRef.value.src = 'about:blank'
    }
    if (flowIframeLoadTimeout) {
      clearTimeout(flowIframeLoadTimeout)
      flowIframeLoadTimeout = null
    }
  }, 200)
}

const handleFlowIframeLoad = () => {
  if (flowIframeLoadTimeout) {
    clearTimeout(flowIframeLoadTimeout)
    flowIframeLoadTimeout = null
  }
  flowIframeLoading.value = false
}

const handleFlowIframeError = () => {
  if (flowIframeLoadTimeout) {
    clearTimeout(flowIframeLoadTimeout)
    flowIframeLoadTimeout = null
  }
  flowIframeLoading.value = false
  ElMessage.error('加载流程查看页失败')
}

const setFlowIframeLoadTimeout = () => {
  if (flowIframeLoadTimeout) clearTimeout(flowIframeLoadTimeout)
  flowIframeLoadTimeout = setTimeout(() => {
    if (flowIframeLoading.value) {
      flowIframeLoading.value = false
      ElMessage.warning('流程页面加载超时，请重试')
    }
  }, 30000)
}

const buildOaFlowDetailUrl = (customModelId, flowId) => {
  const token = getToken()
  const base = '/oa/#/flow/detail'
  const params = new URLSearchParams({
    module_id: String(customModelId),
    flow_id: String(flowId),
    isSinglePage: '1',
    module_name: 'oa',
    form_canal: 'budget'
  })
  if (token) params.set('auth_token', token)
  return `${base}?${params.toString()}`
}

const openFlowDrawer = async (flowId, customModelId) => {
  const id = Number(flowId)
  if (!id || Number.isNaN(id)) return

  let cmid = customModelId ? Number(customModelId) : null
  // plannedExpenditureAPI.getOaFlowDetails 不返回 custom_model_id，缺失时改用 OA 的 view 接口补齐
  if (!cmid || Number.isNaN(cmid)) {
    try {
      const res = await oaFlowAPI.view(id)
      if (res.code === 0 && res.data) {
        cmid = Number(res.data?.customModel?.id || res.data?.flow?.custom_model_id || res.data?.flow?.customModel?.id)
      }
    } catch {
      // ignore
    }
  }
  if (!cmid || Number.isNaN(cmid)) {
    ElMessage.warning('无法打开流程：缺少 custom_model_id')
    return
  }

  flowIframeUrl.value = buildOaFlowDetailUrl(cmid, id)
  flowDrawerVisible.value = true
  flowIframeLoading.value = true
  setFlowIframeLoadTimeout()
}

// 构建完整的文件URL（如果是相对路径，自动拼接当前域名）
const buildFileUrl = (path) => {
  if (!path) return null
  
  const pathStr = String(path).trim()
  if (!pathStr) return null
  
  // 如果已经是完整URL（http:// 或 https://），直接返回
  if (pathStr.startsWith('http://') || pathStr.startsWith('https://')) {
    return pathStr
  }
  
  // 如果是相对路径（以 / 开头），拼接当前域名
  if (pathStr.startsWith('/')) {
    const origin = window.location.origin
    const fullUrl = `${origin}${pathStr}`
    
    if (config.isDevelopment) {
      console.log('[buildFileUrl] 拼接相对路径为完整URL', {
        original: pathStr,
        origin,
        fullUrl
      })
    }
    
    return fullUrl
  }
  
  // 其他情况，尝试拼接（可能是 storage/files/xxx 这种格式）
  if (pathStr.includes('/')) {
    const origin = window.location.origin
    const fullUrl = `${origin}/${pathStr.replace(/^\//, '')}`
    
    if (config.isDevelopment) {
      console.log('[buildFileUrl] 拼接路径为完整URL', {
        original: pathStr,
        origin,
        fullUrl
      })
    }
    
    return fullUrl
  }
  
  // 无法处理的格式，返回原始值
  if (config.isDevelopment) {
    console.warn('[buildFileUrl] 无法处理的路径格式', { path: pathStr })
  }
  
  return pathStr
}

// 附件：兼容字符串/对象/数组
const getFileItems = (field) => {
  const v = getFieldValue(field)
  
  if (config.isDevelopment && (field.element_type === 'file' || field.element_type === 'attachment')) {
    console.log('[getFileItems] 开始处理文件字段', {
      fieldId: field.element_id || field.id,
      fieldKey: field.key || field.field_key,
      rawValue: v,
      rawValueStringified: JSON.stringify(v),
      valueType: typeof v,
      isArray: Array.isArray(v),
      valueLength: Array.isArray(v) ? v.length : (v ? 1 : 0)
    })
  }
  
  if (!v) {
    if (config.isDevelopment && (field.element_type === 'file' || field.element_type === 'attachment')) {
      console.warn('[getFileItems] 字段值为空', { field })
    }
    return []
  }
  
  const toItem = (x, idx) => {
    if (!x) {
      if (config.isDevelopment) {
        console.warn('[getFileItems.toItem] 文件项为空', { idx, x })
      }
      return null
    }
    
    if (config.isDevelopment) {
      console.log('[getFileItems.toItem] 开始处理文件项', {
        idx,
        x,
        xType: typeof x,
        xStringified: JSON.stringify(x),
        xKeys: typeof x === 'object' ? Object.keys(x) : null,
        xValues: typeof x === 'object' ? Object.values(x) : null
      })
    }
    
    if (typeof x === 'string') {
      const s = x.trim()
      if (!s) {
        if (config.isDevelopment) {
          console.warn('[getFileItems.toItem] 字符串为空', { idx, x })
        }
        return null
      }
      const name = s.split('/').pop()
      // 构建完整URL（如果是相对路径会自动拼接域名）
      const url = buildFileUrl(s)
      const result = { name: name || `附件${idx + 1}`, url }
      if (config.isDevelopment) {
        console.log('[getFileItems.toItem] 处理字符串类型', { idx, original: x, result })
      }
      return result
    }
    
    if (typeof x === 'object') {
      if (config.isDevelopment) {
        console.log('[getFileItems.toItem] 处理对象类型 - 开始', {
          idx,
          x,
          xStringified: JSON.stringify(x, null, 2),
          allKeys: Object.keys(x),
          allEntries: Object.entries(x)
        })
      }
      
      // 尝试从多个可能的字段中获取路径
      let rawPath = x.url || x.path || x.file_url || x.download_url || x.href || x.preview_url
      
      if (config.isDevelopment) {
        console.log('[getFileItems.toItem] 第一轮路径查找', {
          idx,
          rawPath,
          checkedFields: {
            url: x.url,
            path: x.path,
            file_url: x.file_url,
            download_url: x.download_url,
            href: x.href,
            preview_url: x.preview_url
          }
        })
      }
      
      // 如果对象中没有路径字段，但对象本身可能就是路径字符串（被包装成对象的情况）
      // 或者检查是否有其他可能包含路径的字段
      if (!rawPath) {
        // 尝试从其他可能的字段中获取
        rawPath = x.value || x.file_path || x.filePath || x.storage_path || x.storagePath || x.folder || x.dir
        
        if (config.isDevelopment) {
          console.log('[getFileItems.toItem] 第二轮路径查找', {
            idx,
            rawPath,
            checkedFields: {
              value: x.value,
              file_path: x.file_path,
              filePath: x.filePath,
              storage_path: x.storage_path,
              storagePath: x.storagePath,
              folder: x.folder,
              dir: x.dir
            }
          })
        }
        
        // 如果还是没有，检查对象的所有值，看是否有字符串类型的路径
        if (!rawPath) {
          for (const [key, value] of Object.entries(x)) {
            if (typeof value === 'string' && (value.includes('/') || value.includes('storage') || value.includes('files'))) {
              rawPath = value
              if (config.isDevelopment) {
                console.log('[getFileItems.toItem] 从对象字段中找到可能的路径', { key, value, rawPath })
              }
              break
            }
          }
        }
      }
      
      // 构建完整URL（如果是相对路径会自动拼接域名）
      const url = rawPath ? buildFileUrl(rawPath) : null
      
      // 尽量还原原始名称
      const name =
        x.original_name ||
        x.file_name ||
        x.name ||
        x.filename ||
        x.originalName ||
        (url ? String(url).split('/').pop() : null) ||
        (rawPath ? String(rawPath).split('/').pop() : null)
      
      if (config.isDevelopment) {
        console.log('[getFileItems.toItem] 提取名称和URL', {
          idx,
          name,
          rawPath,
          url,
          nameSources: {
            original_name: x.original_name,
            file_name: x.file_name,
            name: x.name,
            filename: x.filename,
            originalName: x.originalName
          }
        })
      }
      
      // 如果有名称但没有URL，尝试根据名称构建可能的路径
      let finalUrl = url
      if (!finalUrl && name && rawPath) {
        // 如果 rawPath 是目录路径，尝试拼接文件名
        if (rawPath.endsWith('/') || (!rawPath.includes('.') && (rawPath.includes('storage') || rawPath.includes('files')))) {
          finalUrl = buildFileUrl(`${rawPath.replace(/\/$/, '')}/${name}`)
          if (config.isDevelopment) {
            console.log('[getFileItems.toItem] 尝试拼接目录路径和文件名', {
              idx,
              rawPath,
              name,
              finalUrl
            })
          }
        }
      }
      
      // 如果还是没有URL，但原始值本身可能是路径（检查原始值的字符串表示）
      if (!finalUrl && name) {
        // 检查原始值v（getFieldValue返回的值）是否包含路径信息
        const originalValueStr = JSON.stringify(v)
        if (originalValueStr.includes('/storage/') || originalValueStr.includes('/files/')) {
          // 尝试从原始值中提取路径
          const pathMatch = originalValueStr.match(/["']([^"']*\/storage\/[^"']*|.*\/files\/[^"']*)["']/)
          if (pathMatch && pathMatch[1]) {
            finalUrl = buildFileUrl(pathMatch[1])
            if (config.isDevelopment) {
              console.log('[getFileItems.toItem] 从原始值字符串中提取路径', {
                idx,
                originalValueStr,
                pathMatch: pathMatch[1],
                finalUrl
              })
            }
          }
        }
      }
      
      const result = (finalUrl || name) ? { name: name || `附件${idx + 1}`, url: finalUrl } : null
      
      if (config.isDevelopment) {
        console.log('[getFileItems.toItem] 处理对象类型 - 完成', {
          idx,
          original: x,
          rawPath,
          url: finalUrl,
          name,
          result,
          availableKeys: Object.keys(x),
          allValues: Object.values(x)
        })
      }
      
      if (!result) {
        console.warn('[getFileItems.toItem] 对象中未找到URL或名称', { 
          idx, 
          x, 
          xStringified: JSON.stringify(x, null, 2),
          availableKeys: Object.keys(x),
          allEntries: Object.entries(x)
        })
      } else if (!result.url) {
        console.warn('[getFileItems.toItem] ⚠️ 文件有名称但无URL', { 
          idx, 
          x, 
          xStringified: JSON.stringify(x, null, 2),
          result, 
          availableKeys: Object.keys(x), 
          allValues: Object.values(x),
          rawValue: v,
          rawValueStringified: JSON.stringify(v, null, 2)
        })
      }
      
      return result
    }
    
    if (config.isDevelopment) {
      console.warn('[getFileItems.toItem] 不支持的类型', { idx, x, type: typeof x })
    }
    return null
  }
  
  const arr = Array.isArray(v) ? v : [v]
  const result = arr.map(toItem).filter(Boolean)
  
  if (config.isDevelopment && (field.element_type === 'file' || field.element_type === 'attachment')) {
    console.log('[getFileItems] 处理完成', {
      fieldId: field.element_id || field.id,
      inputArrayLength: arr.length,
      outputLength: result.length,
      result
    })
  }
  
  return result
}

// 处理文件点击
const handleFileClick = (file) => {
  console.log('[handleFileClick] 文件点击事件', {
    file,
    hasUrl: !!file.url,
    url: file.url,
    name: file.name,
    allKeys: Object.keys(file || {})
  })
  
  if (file.url) {
    // 如果有 URL，在新窗口打开
    console.log('[handleFileClick] 打开文件URL', file.url)
    try {
      window.open(file.url, '_blank')
    } catch (error) {
      console.error('[handleFileClick] 打开文件失败', { error, url: file.url })
      ElMessage.error('打开文件失败：' + error.message)
    }
  } else {
    // 如果没有 URL，直接提示用户
    console.warn('[handleFileClick] 文件没有URL', file)
    ElMessage.warning('文件链接不可用')
  }
}

// 格式化默认值
const formatDefaultValue = (value) => {
  if (value === null || value === undefined || value === '') return '-'
  if (typeof value === 'object') {
    try {
      return JSON.stringify(value)
    } catch {
      return String(value)
    }
  }
  return String(value)
}

// -------- checklist：从元素定义取“原始 options”，确保渲染全量可选项 --------
const checklistOptionsMap = ref({}) // elementId -> options[]

const checklistElementIds = computed(() => {
  const cfg = props.template?.config
  const ids = new Set()
  if (!cfg || typeof cfg !== 'object') return []
  Object.values(cfg).forEach(section => {
    if (!section) return
    const pushField = (f) => {
      if (f?.element_type === 'checklist' && f?.element_id) ids.add(Number(f.element_id))
    }
    ;(section.fields || []).forEach(pushField)
    ;(section.rounds || []).forEach(r => (r?.fields || []).forEach(pushField))
  })
  return Array.from(ids).filter(Boolean)
})

const loadChecklistOptions = async (elementId) => {
  const id = Number(elementId)
  if (!id || Number.isNaN(id)) return
  if (checklistOptionsMap.value[id]) return
  try {
    const res = await templateElementAPI.getDetail(id)
    if (res.code === 0 && res.data) {
      checklistOptionsMap.value = {
        ...checklistOptionsMap.value,
        [id]: Array.isArray(res.data.options) ? res.data.options : []
      }
    }
  } catch {
    // ignore
  }
}

watch(
  checklistElementIds,
  async (ids) => {
    for (const id of ids) {
      await loadChecklistOptions(id)
    }
  },
  { immediate: true }
)

const getChecklistOptions = (field) => {
  const id = field?.element_id ? Number(field.element_id) : null
  if (id && checklistOptionsMap.value[id] && checklistOptionsMap.value[id].length) {
    return checklistOptionsMap.value[id]
  }
  return Array.isArray(field?.options) ? field.options : []
}
// -------- detail_table 支持（字段定义 + 人员/部门映射）--------
const detailTableFieldsMap = ref({})
const departmentMap = ref({})
const userMap = ref({})
const loadedDepts = ref(false)
const loadedUsers = ref(false)
const flowDetailsMap = ref({}) // 流程详情缓存：flowId -> { no, title, status, ... }

const loadDepartmentList = async () => {
  if (loadedDepts.value) return
  try {
    const res = await departmentAPI.getTree()
    if (res.code === 0 && Array.isArray(res.data)) {
      const flatten = (depts, out = []) => {
        depts.forEach(d => {
          out.push({ id: d.id, name: d.name })
          if (d.children && d.children.length) flatten(d.children, out)
        })
        return out
      }
      const list = flatten(res.data)
      const map = {}
      list.forEach(d => { map[d.id] = d.name })
      departmentMap.value = map
      loadedDepts.value = true
    }
  } catch {
    // ignore
  }
}

const loadUserList = async () => {
  if (loadedUsers.value) return
  try {
    const res = await userAPI.getSimpleList({ rows: 10000 })
    if (res.code === 0) {
      const users = Array.isArray(res.data) ? res.data : (res.data?.data || [])
      const map = {}
      users.forEach(u => { map[u.id] = u.name })
      userMap.value = map
      loadedUsers.value = true
    }
  } catch {
    // ignore
  }
}

const getDepartmentName = (id) => {
  if (!id) return '-'
  return departmentMap.value[id] || `部门ID: ${id}`
}

const getUserName = (id) => {
  if (!id) return '-'
  return userMap.value[id] || `用户ID: ${id}`
}

const loadDetailTableFields = async (elementId) => {
  if (!elementId) return
  if (detailTableFieldsMap.value[elementId]) return
  try {
    const res = await detailTableFieldAPI.getList(elementId)
    if (res.code === 0 && Array.isArray(res.data)) {
      const sorted = res.data.slice().sort((a, b) => (a.sort_order || 0) - (b.sort_order || 0))
      detailTableFieldsMap.value[elementId] = sorted
      const hasDepartment = sorted.some(f => f.field_type === 'department')
      const hasUser = sorted.some(f => f.field_type === 'user')
      if (hasDepartment) await loadDepartmentList()
      if (hasUser) await loadUserList()
    } else {
      detailTableFieldsMap.value[elementId] = []
    }
  } catch {
    detailTableFieldsMap.value[elementId] = []
  }
}

const detailTableElementIds = computed(() => {
  const cfg = props.template?.config
  const ids = new Set()
  if (!cfg || typeof cfg !== 'object') return []
  Object.values(cfg).forEach(section => {
    if (!section) return
    const pushField = (f) => {
      if (f?.element_type === 'detail_table' && f?.element_id) ids.add(f.element_id)
    }
    ;(section.fields || []).forEach(pushField)
    ;(section.rounds || []).forEach(r => (r?.fields || []).forEach(pushField))
  })
  return Array.from(ids)
})

watch(
  detailTableElementIds,
  async (ids) => {
    for (const id of ids) {
      await loadDetailTableFields(id)
    }
  },
  { immediate: true }
)

// 监听所有流程字段，批量加载流程详情
const allFlowIds = computed(() => {
  const ids = new Set()
  const cfg = props.template?.config
  if (!cfg || typeof cfg !== 'object') return []
  
  Object.values(cfg).forEach(section => {
    if (!section) return
    const collectFlowIds = (f) => {
      if (f?.element_type === 'oa_custom_model') {
        const fieldKey = f?.key || f?.field_key
        const elementId = f?.element_id ? Number(f.element_id) : null
        const bindings = Array.isArray(props.flowBindings) ? props.flowBindings : []
        
        // 从 bindings 中收集流程ID
        const direct = bindings.filter(b => {
          if (!b) return false
          if (elementId && b.element_id && Number(b.element_id) === elementId) return true
          if (fieldKey && b.field_key === fieldKey) return true
          return false
        })
        direct.forEach(b => {
          if (b.flow_instance_id) ids.add(b.flow_instance_id)
        })
        
        // 从字段值中提取流程ID
        const value = getFieldValue(f)
        const extractedIds = extractFlowIds(value)
        extractedIds.forEach(id => ids.add(id))
      }
    }
    ;(section.fields || []).forEach(collectFlowIds)
    ;(section.rounds || []).forEach(r => (r?.fields || []).forEach(collectFlowIds))
  })
  
  return Array.from(ids).filter(Boolean)
})

watch(
  allFlowIds,
  async (ids) => {
    if (ids.length > 0) {
      await loadFlowDetailsBatch(ids)
    }
  },
  { immediate: true }
)

const getDetailTableFields = (field) => {
  const elementId = field?.element_id
  return (elementId && detailTableFieldsMap.value[elementId]) ? detailTableFieldsMap.value[elementId] : []
}

const getDetailTableData = (field) => {
  const v = getFieldValue(field)
  return Array.isArray(v) ? v : []
}

const getAutoKeysForDetailTable = (field) => {
  const rows = getDetailTableData(field)
  const keys = new Set()
  rows.forEach(r => {
    if (r && typeof r === 'object' && !Array.isArray(r)) {
      Object.keys(r).forEach(k => keys.add(k))
    }
  })
  return Array.from(keys)
}

// 判断明细表格值是否为空
const isEmptyDetailTableValue = (val) => {
  if (val === null || val === undefined) return true
  if (typeof val === 'string') return val.trim() === ''
  if (Array.isArray(val)) return val.length === 0
  if (typeof val === 'object') return Object.keys(val).length === 0
  return false
}
</script>

<style scoped>
.template-readonly {
  width: 100%;
}

.template-section {
  margin-bottom: 20px;
}

.section-title {
  font-size: 14px;
  font-weight: bold;
  margin: 0 0 10px 0;
  color: #333;
  padding-bottom: 5px;
  border-bottom: 1px solid #eee;
}

.template-field {
  display: flex;
  margin-bottom: 10px;
  padding: 8px 0;
  border-bottom: 1px dotted #eee;
}

.field-label {
  min-width: 120px;
  font-weight: 500;
  color: #666;
  flex-shrink: 0;
}

.field-value {
  flex: 1;
  color: #333;
  word-break: break-word;
  overflow-x: auto;
}

.empty-value {
  color: #999;
  font-style: italic;
}

/* info-table 样式（与 ContractInfoCard 保持一致） */
.info-table {
  width: 100%;
  border-collapse: collapse;
  table-layout: fixed;
  margin-bottom: 0;
}

.info-table tr {
  border-bottom: 1px solid #f0f2f4;
}

.info-table td {
  padding: 10px 12px;
  vertical-align: top;
  word-break: break-word;
}

.info-table .label {
  width: 120px;
  font-weight: 700;
  color: #111827;
  background: #f9fafb;
}

.info-table .value {
  color: #374151;
}

/* 勾选清单只读样式（与付款基本信息保持一致） */
.readonly-checklist {
  width: 100%;
}

.readonly-checklist .checklist-option-item {
  margin: 1px 0;
}

.readonly-checklist .checklist-option-wrapper {
  display: flex;
  gap: 6px;
  align-items: flex-start;
}

.readonly-checklist .checklist-remark-text {
  color: #6b7280;
  font-size: 12px;
  line-height: 14px;
  padding-top: 0;
}

/* 加深disabled checkbox的颜色（兼容打印，使用黑白灰配色） */
.readonly-checklist :deep(.el-checkbox.is-disabled .el-checkbox__label) {
  color: #111827 !important;
  -webkit-print-color-adjust: exact;
  print-color-adjust: exact;
}

.readonly-checklist :deep(.el-checkbox.is-disabled .el-checkbox__input.is-checked .el-checkbox__inner) {
  background-color: #111827 !important;
  border-color: #111827 !important;
  -webkit-print-color-adjust: exact;
  print-color-adjust: exact;
}

.readonly-checklist :deep(.el-checkbox.is-disabled .el-checkbox__input.is-checked .el-checkbox__inner::after) {
  border-color: #ffffff !important;
  -webkit-print-color-adjust: exact;
  print-color-adjust: exact;
}

.readonly-checklist :deep(.el-checkbox.is-disabled .el-checkbox__inner) {
  border-color: #909399 !important;
  background-color: #ffffff !important;
  -webkit-print-color-adjust: exact;
  print-color-adjust: exact;
}

.checklist-label {
  color: #111827;
  line-height: 18px;
  word-break: break-word;
}

.checklist-text {
  flex: 1;
  min-width: 0;
  line-height: 18px;
  word-break: break-word;
}

/* 纯展示的 checkbox 样式（非表单控件，无 disabled 灰态） */
.checklist-box {
  width: 14px;
  height: 14px;
  border: 1px solid #111827;
  border-radius: 3px;
  margin-top: 2px;
  flex-shrink: 0;
  position: relative;
  background: transparent;
}
.checklist-box.checked {
  border-color: #111827;
  background: transparent;
}
.checklist-box.checked::after {
  content: '';
  position: absolute;
  left: 50%;
  top: 50%;
  width: 6px;
  height: 10px;
  border-right: 2px solid #111827;
  border-bottom: 2px solid #111827;
  /* 先居中再旋转；Y 方向略上提一点，视觉更“正中” */
  transform: translate(-50%, -58%) rotate(45deg);
}

.checklist-fallback-item {
  margin: 4px 0;
  color: #333;
}

/* 明细表格只读样式 */
.detail-table-readonly {
  width: 100%;
}

/* 明细表格卡片样式 */
.detail-table-cards {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.detail-table-card {
  border: 1px solid #e5e7eb;
  border-radius: 6px;
  background: #f9fafb;
  overflow: hidden;
  page-break-inside: avoid;
}

.detail-table-card__header {
  padding: 8px 12px;
  background: #f3f4f6;
  border-bottom: 1px solid #e5e7eb;
  font-weight: 600;
  font-size: 13px;
  color: #374151;
}

.detail-table-card__index {
  color: #6b7280;
}

.detail-table-card__body {
  padding: 10px 12px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.detail-table-card__field {
  display: flex;
  align-items: flex-start;
  line-height: 1.5;
}

.detail-table-card__label {
  min-width: 100px;
  font-weight: normal;
  color: #6b7280;
  flex-shrink: 0;
  margin-right: 8px;
}

.detail-table-card__value {
  flex: 1;
  color: #374151;
  word-break: break-word;
  font-weight: normal;
}

/* 打印时优化卡片样式 */
@media print {
  .detail-table-card {
    border: 1px solid #d1d5db;
    margin-bottom: 8px;
    page-break-inside: avoid;
  }
  
  .detail-table-card__header {
    background: #f3f4f6;
    border-bottom: 1px solid #d1d5db;
  }
}

.flow-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.flow-tag {
  max-width: 100%;
}

.flow-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.flow-item {
  display: flex;
  align-items: center;
  gap: 10px;
}
.flow-item__title {
  flex: 1;
  min-width: 0;
  color: #111827;
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.flow-item__title--link {
  cursor: pointer;
}

.flow-detail__fields {
  margin-top: 12px;
}

/* 审批流程显示样式（与付款基本信息保持一致） */
.approval-flow-display {
  width: 100%;
}

.approval-flow-display .flow-info {
  display: inline;
}

/* 流程信息可点击样式（复用付款基本信息的样式） */
.flow-info-clickable {
  cursor: pointer;
  color: #409eff;
  text-decoration: none;
  transition: color 0.2s;
}

.flow-info-clickable:hover {
  color: #66b1ff;
  text-decoration: underline;
}

/* 打印时隐藏hover效果 */
@media print {
  .flow-info-clickable {
    cursor: default;
    color: inherit;
    text-decoration: none;
  }
}

/* 附件显示样式（与付款基本信息保持一致） */
.attachment-display {
  width: 100%;
}

.file-separator {
  margin: 0 4px;
  color: #909399;
}

.muted {
  color: #909399;
}

.file-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.file-item {
  line-height: 18px;
}
.file-link {
  cursor: pointer;
}
.file-link--no-url {
  cursor: pointer;
}
.file-link--no-url:hover {
  text-decoration: underline;
}
.file-name {
  color: #374151;
}

.drawer-content {
  min-height: 60vh;
}
.oa-flow-iframe {
  width: 100%;
  height: 75vh;
  border: 0;
}

/* 支付轮次样式 */
.payment-round {
  margin-bottom: 15px;
  padding: 10px;
  background: #fafafa;
  border-radius: 4px;
}

.round-title {
  font-size: 13px;
  font-weight: bold;
  margin: 0 0 10px 0;
  color: #409eff;
}

/* 打印样式 */
@media print {
  .template-section {
    page-break-inside: avoid;
  }

  .payment-round {
    page-break-inside: avoid;
  }
}
</style>
















