<template>
  <div class="smart-triage-queue">
    <!-- 顶部标题栏 -->
    <div class="header-section">
      <div class="header-left">
            <span class="title">智能分诊排队管理 - {{ currentDeptName }}</span>
      </div>
      <div class="header-right">
        <el-button type="primary" @click="handleRefresh">
          <el-icon><Refresh /></el-icon>
          刷新
        </el-button>
        <el-button @click="handleExit">退出</el-button>
        <el-button @click="handleConfig">后台配置</el-button>
      </div>
    </div>

    <!-- 主要内容区域 -->
    <div class="main-content">
      <!-- 左侧：智能候选池 -->
      <div class="left-panel">
        <div class="panel-header">
          <span class="panel-title">① 智能候选池 (已签到未入队)</span>
        </div>
        <div class="table-container">
          <el-table
            :data="filteredCandidatePoolList"
            stripe
            border
            height="100%"
            style="width: 100%"
            @selection-change="handleCandidateSelectionChange"
          >
            <el-table-column type="selection" width="55" align="center" />
            <el-table-column prop="sequenceNo" label="序号" width="80" align="center" />
            <el-table-column prop="patientName" label="患者" width="100" align="center" />
            <el-table-column prop="age" label="年龄" width="80" align="center" />
            <el-table-column prop="appointmentType" label="号别" width="100" align="center" />
            <el-table-column prop="room" label="诊室" width="120" align="center" />
            <el-table-column prop="doctor" label="医生" width="120" align="center" />
            <el-table-column prop="matchingRule" label="命中规则" min-width="150" align="center" />
          </el-table>
        </div>
        <div class="candidate-actions">
          <el-button 
            type="primary" 
            @click="handleAddToQueue"
            :disabled="selectedCandidates.length === 0"
          >
            加入队列 >>
          </el-button>
          <el-button 
            type="primary" 
            @click="handleAddAllToQueue"
            :disabled="filteredCandidatePoolList.length === 0"
          >
            一键加入队列
          </el-button>
        </div>
      </div>

      <!-- 右侧：智能队列 -->
      <div class="right-panel">
        <div class="panel-header">
          <span class="panel-title">② 智能队列 (全科)</span>
        </div>
        <div class="table-container">
          <el-table
            :data="filteredQueueList"
            stripe
            border
            height="100%"
            style="width: 100%"
            :row-class-name="getRowClassName"
            highlight-current-row
            @row-click="handleQueueRowClick"
          >
            <el-table-column prop="queueOrder" label="队序" width="80" align="center" />
            <el-table-column prop="patientName" label="患者" width="100" align="center" />
            <el-table-column prop="appointmentType" label="号别" width="100" align="center" />
            <el-table-column prop="room" label="诊室" width="120" align="center" />
            <el-table-column prop="doctor" label="医生" width="120" align="center" />
            <el-table-column prop="waitingTime" label="等待" width="100" align="center" />
            <el-table-column prop="status" label="状态" width="100" align="center">
              <template #default="scope">
                <el-tag :type="getStatusTagType(scope.row.status)">
                  {{ scope.row.status }}
                </el-tag>
              </template>
            </el-table-column>
          </el-table>
        </div>
        <div class="display-options">
          <div class="queue-actions-left">
            <el-button 
              type="danger" 
              @click="handleRemoveFromQueue"
              :disabled="!selectedQueueRow"
              size="small"
            >
              << 移出队列
            </el-button>
            <el-button 
              type="info" 
              @click="handleMoveUp"
              :disabled="!selectedQueueRow || !canMoveUp"
              size="small"
            >
              ↑
            </el-button>
            <el-button 
              type="info" 
              @click="handleMoveDown"
              :disabled="!selectedQueueRow || !canMoveDown"
              size="small"
            >
              ↓
            </el-button>
          </div>
          <div class="queue-actions-right">
            <el-button 
              :type="showOnlyWaiting ? 'primary' : ''" 
              @click="showOnlyWaiting = true"
              size="small"
            >
              只显示等待
            </el-button>
            <el-button 
              :type="!showOnlyWaiting ? 'primary' : ''" 
              @click="showOnlyWaiting = false"
              size="small"
            >
              显示全部状态
            </el-button>
          </div>
        </div>
      </div>
    </div>

    <!-- 底部控制面板 -->
    <div class="footer-section">
      <!-- 就诊科室快速过滤栏 -->
      <div class="filter-section">
        <div class="filter-label">③ 就诊科室快速过滤栏</div>
        <div class="filter-select-wrapper">
          <el-select
            v-model="selectedDept"
            placeholder="请选择就诊科室"
            clearable
            filterable
            style="width: 100%"
            size="default"
          >
            <el-option
              label="全部"
              value="all"
            />
            <el-option
              v-for="dept in departmentList"
              :key="dept.id"
              :label="dept.name"
              :value="dept.id"
            />
          </el-select>
        </div>
      </div>

      <!-- 叫号控制板 -->
      <div class="call-control-section">
        <div class="call-control-label">④ 叫号控制板</div>
        <div class="call-control-content">
          <div class="current-call-display">
            当前呼叫: {{ currentCall.number }} {{ currentCall.name }} 诊室: {{ currentCall.room }}
          </div>
          <div class="control-buttons">
            <el-button type="primary" @click="handleSelectCall">选呼</el-button>
            <el-button type="success" @click="handleNextPatient">下一患者</el-button>
            <el-button type="warning" @click="handleSkip">跳过</el-button>
            <el-button type="primary" @click="handleComplete">完成</el-button>
            <el-button type="info" @click="handleRequeue">过号重排</el-button>
          </div>
        </div>
      </div>

      <!-- LED显示 -->
      <div class="led-section">
        <div class="led-label">⑤ LED:</div>
        <div class="led-display">
          [{{ currentCall.number }}]{{ currentCall.name }}请到{{ currentCall.room }}({{ callType }})
        </div>
      </div>

      <!-- 语音提示 -->
      <div class="voice-section">
        <span class="voice-label">语音:</span>
        <span class="voice-text">请{{ currentCall.number }}号{{ currentCall.name }}到{{ currentCall.room }}({{ callType }})</span>
      </div>
    </div>

  <!-- 后台配置弹窗 -->
  <el-dialog
    v-model="configDialogVisible"
    width="90%"
    top="5vh"
  >
    <template #header>
      <div class="config-dialog-header">
        <div class="config-dialog-title">智能分诊规则引擎配置 - 心内科</div>
        <div class="config-topbar-actions">
          <el-button type="primary" @click="handleAddRule">新增规则</el-button>
          <el-button type="primary" @click="handleSaveAllRules">保存全部</el-button>
          <el-button @click="handleTestRule">测试规则</el-button>
        </div>
      </div>
    </template>

    <div class="config-container">
      <div class="config-left">
        <el-scrollbar height="560px">
          <div
            v-for="(item, idx) in rules"
            :key="idx"
            class="rule-card"
            :class="{ active: idx === editingIndex }"
            @click="handleSelectRule(idx)"
          >
            <div class="rule-title">规则{{ idx + 1 }}</div>
            <div class="rule-sub">prio={{ item.priority }}</div>
            <div class="rule-sub">{{ item.name }}</div>
            <div class="rule-actions">
              <el-button size="small" @click.stop="handleSelectRule(idx)">编辑</el-button>
              <el-button size="small" @click.stop="handleDeleteRule(idx)">删除</el-button>
              <el-button size="small" @click.stop="handleRuleMoveUp(idx)" :disabled="idx === 0">↑</el-button>
              <el-button size="small" @click.stop="handleRuleMoveDown(idx)" :disabled="idx === rules.length - 1">↓</el-button>
            </div>
          </div>
        </el-scrollbar>
      </div>

      <div class="config-right">
        <el-form label-width="110px" class="config-form">
          <el-form-item label="规则名称:" required>
            <el-input v-model="ruleForm.name" placeholder="请输入规则名称" />
          </el-form-item>

          <el-form-item label="科室:">
            <el-select v-model="ruleForm.dept" class="config-fullwidth" disabled>
              <el-option label="心内科" value="心内科" />
            </el-select>
          </el-form-item>

          <el-form-item label="规则描述:">
            <el-input v-model="ruleForm.desc" placeholder="请输入规则描述" />
          </el-form-item>

          <el-form-item label="优先级:" required>
            <el-input v-model="ruleForm.priority" />
          </el-form-item>

          <el-form-item label="周几生效:">
            <el-checkbox-group v-model="ruleForm.weeks">
              <el-checkbox v-for="w in weekOptions" :key="w.value" :label="w.value">
                {{ w.label }}
              </el-checkbox>
            </el-checkbox-group>
          </el-form-item>

          <el-form-item label="条件表达式(JSON):">
            <el-input
              v-model="ruleForm.expr"
              type="textarea"
              :rows="8"
              placeholder='{"age":">=60","regType":"专家"}'
            />
          </el-form-item>

          <div class="config-inline-actions">
            <el-button @click="handleQuickGenerate">快速生成器</el-button>
            <el-button @click="handleValidateRule">语法检查</el-button>
          </div>
        </el-form>
      </div>
    </div>

    <template #footer>
      <div class="config-footer">
        <el-button type="primary" @click="handleSaveCurrentRule">保存</el-button>
        <el-button @click="configDialogVisible = false">取消</el-button>
      </div>
    </template>
  </el-dialog>

  <!-- 快速生成器对话框 -->
  <el-dialog
    v-model="quickGeneratorDialogVisible"
    title="快速生成器"
    width="600px"
    :close-on-click-modal="false"
  >
    <el-form :model="quickGeneratorForm" label-width="120px">
      <el-form-item label="年龄条件:">
        <div style="display: flex; align-items: center; gap: 10px;">
          <el-select v-model="quickGeneratorForm.ageOperator" style="width: 100px;">
            <el-option label=">=" value=">=" />
            <el-option label="<=" value="<=" />
            <el-option label="=" value="=" />
            <el-option label=">" value=">" />
            <el-option label="<" value="<" />
          </el-select>
          <el-input-number
            v-model="quickGeneratorForm.ageValue"
            :min="0"
            :max="150"
            :precision="0"
            placeholder="年龄"
            style="width: 150px;"
          />
          <el-button
            type="text"
            @click="quickGeneratorForm.ageOperator = null; quickGeneratorForm.ageValue = null"
          >
            清除
          </el-button>
        </div>
      </el-form-item>

      <el-form-item label="号别/类型:">
        <el-select
          v-model="quickGeneratorForm.regType"
          placeholder="请选择号别"
          clearable
          style="width: 100%"
        >
          <el-option label="专家" value="专家" />
          <el-option label="普通" value="普通" />
          <el-option label="特需" value="特需" />
          <el-option label="急诊" value="急诊" />
        </el-select>
      </el-form-item>

      <el-form-item label="科室:">
        <el-select
          v-model="quickGeneratorForm.dept"
          placeholder="请选择科室"
          clearable
          style="width: 100%"
        >
          <el-option label="心内科" value="心内科" />
          <el-option label="心外科" value="心外科" />
          <el-option label="神经内科" value="神经内科" />
        </el-select>
      </el-form-item>

      <el-form-item label="医生:">
        <el-input
          v-model="quickGeneratorForm.doctor"
          placeholder="请输入医生姓名（支持模糊匹配）"
          clearable
        />
      </el-form-item>

      <el-form-item label="自定义条件:">
        <el-input
          v-model="quickGeneratorForm.customKey"
          placeholder="字段名（如：gender）"
          style="width: 150px; margin-right: 10px;"
        />
        <el-input
          v-model="quickGeneratorForm.customValue"
          placeholder="字段值（如：男）"
          style="width: 200px;"
        />
        <el-button
          type="text"
          @click="quickGeneratorForm.customKey = ''; quickGeneratorForm.customValue = ''"
        >
          清除
        </el-button>
      </el-form-item>

      <el-form-item label="预览JSON:">
        <el-input
          :value="previewJson"
          type="textarea"
          :rows="4"
          readonly
          style="font-family: monospace;"
        />
      </el-form-item>
    </el-form>

    <template #footer>
      <div style="text-align: right;">
        <el-button @click="quickGeneratorDialogVisible = false">取消</el-button>
        <el-button type="primary" @click="handleApplyQuickGenerate">应用</el-button>
      </div>
    </template>
  </el-dialog>
  </div>
</template>

<script setup>
import { ref, reactive, computed, onMounted, onUnmounted } from 'vue'
import useUserStore from '@/store/modules/user'

const userStore = useUserStore()
import { Refresh } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'
import {
  getCandidatePool,
  getLocationTree,
  getTriageQueueList,
  addToQueue,
  removeFromQueue,
  adjustQueueOrder,
  callPatient,
  skipPatient,
  completeCall,
  requeuePatient,
  nextPatient
} from '../api'

// 当前日期 & 统计信息（总已签到/在队列中）
const currentDate = ref('2025/12/22 上午')
const totalSignedIn = ref(0)
const totalInQueue = ref(0)

// 当前呼叫信息
const currentCall = ref({
  number: '1',
  name: '郑华',
  room: '4号诊室'
})

// 当前选中的队列行（用于选呼）
const selectedQueueRow = ref(null)

// 选中的候选池患者（多选）
const selectedCandidates = ref([])

// 显示选项
const showOnlyWaiting = ref(false)

// 科室过滤（改为使用就诊科室）
const selectedDept = ref('all')
// 就诊科室列表
const departmentList = ref([])

// 修复【#397】：动态获取当前科室名称
const currentDeptName = computed(() => {
  return userStore.deptName || userStore.orgName || '心内科'
})

// 后台配置对话框
const configDialogVisible = ref(false)
const rules = ref([
  {
    name: '老年专家优先',
    dept: '心内科',
    desc: '老年专家优先',
    priority: 10,
    weeks: ['1', '2', '3', '4', '5'],
    expr: '{"age":">=60","regType":"专家"}'
  },
  {
    name: '少年优先',
    dept: '心内科',
    desc: '少年优先',
    priority: 20,
    weeks: ['1', '2', '3', '4', '5'],
    expr: '{"age":"<18"}'
  },
  {
    name: '默认',
    dept: '心内科',
    desc: '默认',
    priority: 50,
    weeks: ['1', '2', '3', '4', '5', '6', '0'],
    expr: '{}'
  }
])
const editingIndex = ref(0)
const ruleForm = reactive({
  name: '',
  dept: '心内科',
  desc: '',
  priority: 10,
  weeks: ['1', '2', '3', '4', '5'],
  expr: ''
})

// 快速生成器对话框
const quickGeneratorDialogVisible = ref(false)
const quickGeneratorForm = reactive({
  ageOperator: null, // >=, <=, =, >, <
  ageValue: null,
  regType: null, // 号别
  dept: null, // 科室
  doctor: null, // 医生
  customKey: '', // 自定义字段名
  customValue: '' // 自定义字段值
})
const weekOptions = [
  { label: '一', value: '1' },
  { label: '二', value: '2' },
  { label: '三', value: '3' },
  { label: '四', value: '4' },
  { label: '五', value: '5' },
  { label: '六', value: '6' },
  { label: '日', value: '0' }
]

// 叫号类型
const callType = ref('选呼')

// ============ 数据初始化/刷新（前端模拟） ============
const getInitialCandidatePoolList = () => ([
  {
    sequenceNo: 12,
    patientName: '陈明',
    age: 65,
    appointmentType: '专家',
    room: '3号诊室',
    doctor: '张医生',
    matchingRule: '年龄≥60'
  },
  {
    sequenceNo: 13,
    patientName: '刘芳',
    age: 58,
    appointmentType: '普通',
    room: '4号诊室',
    doctor: '李医生',
    matchingRule: '-'
  },
  {
    sequenceNo: 14,
    patientName: '周强',
    age: 45,
    appointmentType: '普通',
    room: '5号诊室',
    doctor: '王医生',
    matchingRule: '-'
  },
  {
    sequenceNo: 15,
    patientName: '吴伟',
    age: 72,
    appointmentType: '专家',
    room: '3号诊室',
    doctor: '张医生',
    matchingRule: '年龄≥70'
  }
])

const getInitialQueueList = () => ([
  {
    queueOrder: 1,
    patientName: '林静',
    appointmentType: '专家',
    room: '3号诊室',
    doctor: '张医生',
    waitingTime: '05:00',
    status: '等待'
  },
  {
    queueOrder: 2,
    patientName: '郑华',
    appointmentType: '普通',
    room: '4号诊室',
    doctor: '李医生',
    waitingTime: '00:00',
    status: '叫号中'
  },
  {
    queueOrder: 3,
    patientName: '王丽',
    appointmentType: '普通',
    room: '5号诊室',
    doctor: '王医生',
    waitingTime: '08:00',
    status: '等待'
  },
  {
    queueOrder: 4,
    patientName: '张伟',
    appointmentType: '专家',
    room: '3号诊室',
    doctor: '张医生',
    waitingTime: '12:00',
    status: '等待'
  }
])

const syncCurrentCallFromQueue = () => {
  const calling = originalQueueList.value.find((i) => i.status === '叫号中')
  if (!calling) {
    currentCall.value = { number: '-', name: '-', room: '-' }
    selectedQueueRow.value = null
    return
  }
  currentCall.value = {
    number: String(calling.queueOrder),
    name: calling.patientName,
    room: calling.room
  }
  selectedQueueRow.value = calling
}

// ============ 与后端接口联动 ============
// 注意：后端未实现或出错时会自动退回本地假数据
// 将分钟数转换为 MM:SS 格式
const formatMinutesToMmSs = (minutes) => {
  if (minutes == null || minutes < 0) return '00:00'
  const hours = Math.floor(minutes / 60)
  const mins = minutes % 60
  const hoursStr = String(hours).padStart(2, '0')
  const minsStr = String(mins).padStart(2, '0')
  return `${hoursStr}:${minsStr}`
}

// 将年龄字符串转换为数字（处理 "20岁"、"1小时" 等格式）
const parseAge = (ageStr) => {
  if (!ageStr) return 0
  const match = ageStr.match(/(\d+)/)
  return match ? parseInt(match[1], 10) : 0
}

// 后端队列状态 -> 前端展示状态
const mapBackendStatusToFrontend = (status) => {
  if (!status) {
    console.warn('【心内科】状态映射：收到空状态值')
    return '等待'
  }
  // 转换为大写并去除空格，确保匹配
  const normalizedStatus = String(status).trim().toUpperCase()
  if (normalizedStatus === 'CALLING') return '叫号中'
  if (normalizedStatus === 'WAITING') return '等待'
  if (normalizedStatus === 'SKIPPED') return '跳过'
  if (normalizedStatus === 'COMPLETED') return '已完成'
  console.warn('【心内科】状态映射：未知状态值', status, '-> 默认返回"等待"')
  return '等待'
}

// 前端状态 -> 后端状态（目前仅展示用）
const mapFrontendStatusToBackend = (status) => {
  if (!status) return 'WAITING'
  if (status === '叫号中') return 'CALLING'
  if (status === '等待') return 'WAITING'
  if (status === '跳过') return 'SKIPPED'
  if (status === '已完成') return 'COMPLETED'
  return 'WAITING'
}

// 从数据库加载队列
const loadQueueFromDb = async () => {
  try {
    // 如果选择了具体科室，就按科室加载；否则加载当前登录人科室（后端默认）
    const organizationId = selectedDept.value !== 'all' ? selectedDept.value : undefined
    // 只查询今天的患者
    const today = new Date()
    const todayStr = `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, '0')}-${String(today.getDate()).padStart(2, '0')}`
    console.log('【心内科】loadQueueFromDb 开始：organizationId=', organizationId, 'date=', todayStr, 'selectedDept=', selectedDept.value)
    const res = await getTriageQueueList({ organizationId, date: todayStr }).catch((err) => {
      console.error('【心内科】loadQueueFromDb 请求异常：', err)
      return { code: 500, msg: err?.message || '请求失败', data: null }
    })
    console.log('【心内科】loadQueueFromDb 后端响应：', res)
    
    // 检查响应是否成功
    if (res?.code !== 200 && res?.code !== undefined) {
      console.error('【心内科】loadQueueFromDb 后端返回错误：', res?.code, res?.msg)
      // 即使后端返回错误，也不清空队列，避免误删数据
      // originalQueueList.value 保持原值
      return
    }
    
    const list = Array.isArray(res?.data) ? res.data : []
    console.log('【心内科】loadQueueFromDb 从后端获取到', list.length, '条队列数据')
    if (list.length === 0) {
      console.log('【心内科】提示：队列数据为空（可能是今天还没有患者加入队列）')
    }

    originalQueueList.value = list
      .map((it) => {
        const frontendStatus = mapBackendStatusToFrontend(it.status)
        // 调试日志：检查状态映射
        if (list.length <= 5) {
          console.log('【心内科】状态映射：后端状态=', it.status, '-> 前端状态=', frontendStatus, '患者=', it.patientName)
        }
        // 计算等待时间：基于创建时间（createTime）
        let waitingTime = '00:00'
        if (it.createTime) {
          try {
            const createTime = new Date(it.createTime)
            // 检查日期是否有效
            if (!isNaN(createTime.getTime())) {
              const now = new Date()
              const diffSeconds = Math.floor((now - createTime) / 1000)
              waitingTime = formatSecondsToMmSs(Math.max(0, diffSeconds))
            } else {
              console.warn('【心内科】无效的创建时间:', it.createTime, '患者:', it.patientName)
            }
          } catch (e) {
            console.error('【心内科】解析创建时间失败:', it.createTime, '患者:', it.patientName, e)
          }
        }
        
        return {
          id: it.id,
          queueOrder: it.queueOrder,
          patientName: it.patientName ?? '-',
          // 队列数据已从入队时存储的 healthcareName 读取（包含"预约"或"挂号"后缀）
          appointmentType: it.healthcareName ?? '普通号挂号',
          // Bug #410：使用 roomNo（诊室）而非 organizationName（科室）
          room: it.roomNo ?? it.organizationName ?? '-',
          doctor: it.practitionerName ?? '-',
          waitingTime: waitingTime,
          createTime: it.createTime, // 保存创建时间，用于定时器计算
          status: frontendStatus,
          // 关联字段（用于去重/追溯）
          encounterId: it.encounterId,
          organizationId: it.organizationId
        }
      })
      .filter((item) => {
        // 过滤掉"已完成"状态的患者，不显示在队列中
        if (item.status === '已完成') {
          console.log('【心内科】过滤掉已完成状态的患者：', item.patientName)
          return false
        }
        return true
      })
    
    // 调试日志：检查查找结果
    const callingCount = originalQueueList.value.filter(i => i.status === '叫号中').length
    const waitingCount = originalQueueList.value.filter(i => i.status === '等待').length
    console.log('【心内科】队列状态统计：总数=', originalQueueList.value.length, '叫号中=', callingCount, '等待=', waitingCount)
    if (originalQueueList.value.length > 0) {
      console.log('【心内科】队列中的 encounterIds:', originalQueueList.value.map(q => ({ name: q.patientName, encounterId: q.encounterId })))
    }

    // 统计
    totalInQueue.value = originalQueueList.value.length
    syncCurrentCallFromQueue()
  } catch (e) {
    console.error('【心内科】loadQueueFromDb 异常：', e)
    // 不回退本地假数据，避免“看起来有数据但实际没落库”的误导
    originalQueueList.value = []
    totalInQueue.value = 0
    syncCurrentCallFromQueue()
  }
}

// 根据状态枚举文本转换为前端状态
const mapStatusToFrontend = (statusEnumText) => {
  if (!statusEnumText) return '等待'
  // 状态映射：就诊中 -> 叫号中，其他 -> 等待
  if (statusEnumText.includes('就诊中') || statusEnumText.includes('在诊')) {
    return '叫号中'
  } else if (statusEnumText.includes('已完成') || statusEnumText.includes('已出院')) {
    return '已完成'
  } else if (statusEnumText.includes('已取消') || statusEnumText.includes('退号')) {
    return '已取消'
  }
  return '等待'
}

// 判断日期是否是今天
const isToday = (date) => {
  if (!date) return false
  const targetDate = new Date(date)
  const today = new Date()
  return targetDate.getFullYear() === today.getFullYear() &&
         targetDate.getMonth() === today.getMonth() &&
         targetDate.getDate() === today.getDate()
}

// 过滤今天的数据
const filterTodayData = (data) => {
  if (!Array.isArray(data)) return []
  const today = new Date()
  today.setHours(0, 0, 0, 0)
  const tomorrow = new Date(today)
  tomorrow.setDate(tomorrow.getDate() + 1)
  
  return data.filter(item => {
    if (!item.registerTime) return false
    const registerDate = new Date(item.registerTime)
    return registerDate >= today && registerDate < tomorrow
  })
}

const loadDataFromApi = async () => {
  // 明确打日志，方便你在浏览器 Console 里看到是否调用到了这里
  console.log('【心内科】loadDataFromApi 被调用了：候选池=门诊挂号接口，队列=数据库接口')
  try {
    const baseParams = {}

    // 1) 候选池（使用门诊挂号当日已挂号接口）

    const candidateRes = await getCandidatePool(baseParams).catch((err) => {

      return null
    })

    if (candidateRes && candidateRes.data != null) {
      // 门诊挂号接口返回的是分页数据 { records: [], total: 0 }
      const records = candidateRes.data.records || candidateRes.data
      const data = Array.isArray(records) ? records : (Array.isArray(candidateRes.data) ? candidateRes.data : [])
      console.log('【心内科】候选池数据：', data, '类型：', typeof data, '是否为数组：', Array.isArray(data), '数量：', data.length)
      if (Array.isArray(data) && data.length > 0) {
        // 过滤今天的数据
        const todayData = filterTodayData(data)
        console.log('【心内科】过滤后的今天数据：', todayData.length, '条（原始：', data.length, '条）')
        // 门诊挂号 CurrentDayEncounterDto → 候选池
        originalCandidatePoolList.value = todayData.map((item, idx) => ({
          sequenceNo: idx + 1,
          encounterId: item.encounterId,
          patientId: item.patientId,
          organizationId: item.organizationId,
          organizationName: item.organizationName,
          patientName: item.patientName ?? '-',
          age: parseAge(item.age),
          // Bug #409/410：根据 isFromAppointment 区分预约/挂号，使用 clinicRoom 诊室
          appointmentType: (item.healthcareName || '').replace('挂号', '') + (item.isFromAppointment ? '预约' : '挂号'),
          room: item.clinicRoom ?? item.organizationName ?? '-',
          doctor: item.practitionerName ?? '-',
          // 当前接口返回的是 practitionerUserId，保存为 practitionerId 供入队使用
          practitionerId: item.practitionerUserId ?? null,
          matchingRule: '-', // 这里先不做智能规则匹配
          // 号源池和槽位信息（用于分诊队列）
          poolId: item.poolId ?? null,
          slotId: item.slotId ?? null,
          // 保存原始 isFromAppointment 用于调试
          isFromAppointment: item.isFromAppointment ?? false
        }))
        console.log('【心内科】候选池已加载', originalCandidatePoolList.value.length, '条今天的数据')
      } else {
        console.log('【心内科】候选池数据为空数组或非数组，使用默认数据')
        originalCandidatePoolList.value = getInitialCandidatePoolList()
      }
    } else {
      console.log('【心内科】候选池响应为空或格式错误，使用默认数据')
      originalCandidatePoolList.value = getInitialCandidatePoolList()
    }

    // 2) 队列列表：从数据库读取（可刷新、可恢复）
    await loadQueueFromDb()

    // 3) 候选池去重：已经在队列里的患者（按 encounterId）不再出现在候选池
    // 注意：确保 encounterId 类型一致（统一转换为字符串进行比较，避免数字和字符串不匹配）
    const queueEncounterIds = new Set(
      originalQueueList.value
        .map((q) => q.encounterId)
        .filter((id) => id != null && id !== undefined && id !== '')
        .map((id) => String(id)) // 统一转换为字符串
    )
    console.log('【心内科】去重前：候选池', originalCandidatePoolList.value.length, '条，队列', originalQueueList.value.length, '条，队列encounterIds=', Array.from(queueEncounterIds))
    if (queueEncounterIds.size > 0) {
      const beforeCount = originalCandidatePoolList.value.length
      originalCandidatePoolList.value = originalCandidatePoolList.value.filter((c) => {
        const candidateId = c.encounterId != null ? String(c.encounterId) : null
        return candidateId == null || !queueEncounterIds.has(candidateId)
      })
      console.log('【心内科】去重后：候选池', originalCandidatePoolList.value.length, '条（移除了', beforeCount - originalCandidatePoolList.value.length, '条）')
      // 如果去重后数量没有变化，可能是类型不匹配问题
      if (beforeCount === originalCandidatePoolList.value.length && beforeCount > 0) {
        console.warn('【心内科】警告：去重前后数量未变化，可能是 encounterId 类型不匹配！')
        console.warn('  候选池中的 encounterIds:', originalCandidatePoolList.value.slice(0, 3).map(c => ({ name: c.patientName, id: c.encounterId, type: typeof c.encounterId })))
        console.warn('  队列中的 encounterIds:', originalQueueList.value.slice(0, 3).map(q => ({ name: q.patientName, id: q.encounterId, type: typeof q.encounterId })))
      }
    } else {
      console.warn('【心内科】警告：队列中没有 encounterId，无法进行去重！')
      if (originalQueueList.value.length > 0) {
        console.warn('  队列数据详情:', originalQueueList.value.slice(0, 3).map(q => ({ name: q.patientName, encounterId: q.encounterId })))
      }
    }

    // 4) 统计信息（只统计候选池和队列的数据）
    totalSignedIn.value = originalCandidatePoolList.value.length
    console.log('【心内科】统计信息：候选池', totalSignedIn.value, '条，队列', totalInQueue.value, '条')

    // 当前日期展示（简单用本机时间）
    const now = new Date()
    currentDate.value = `${now.getFullYear()}/${now.getMonth() + 1}/${now.getDate()}`

    // 同步当前呼叫（队列从 DB 加载后已同步；这里再兜底一次）
    syncCurrentCallFromQueue()
    console.log('【心内科】数据加载完成：候选池', originalCandidatePoolList.value.length, '条，队列', originalQueueList.value.length, '条')
    ElMessage.success('【心内科】已从门诊挂号接口加载数据')
  } catch (e) {
    console.error('【心内科】loadDataFromApi 执行异常，使用本地假数据：', e)
    // 任何异常：回退本地假数据
    originalCandidatePoolList.value = getInitialCandidatePoolList()
    // 队列不再回退假数据，避免误导
    originalQueueList.value = []
    totalSignedIn.value = originalCandidatePoolList.value.length
    totalInQueue.value = originalQueueList.value.length
    syncCurrentCallFromQueue()
  }
}

// 原始数据存储（用于过滤）
const originalCandidatePoolList = ref(getInitialCandidatePoolList())

// 辅助函数：扁平化科室树形结构
const flattenDepartmentTree = (tree, result = []) => {
  if (!Array.isArray(tree)) return result
  tree.forEach(node => {
    if (node.id && node.name) {
      result.push({ id: node.id, name: node.name })
    }
    if (node.children && Array.isArray(node.children)) {
      flattenDepartmentTree(node.children, result)
    }
  })
  return result
}

// 加载就诊科室列表
const loadDepartmentList = async () => {
  try {
    const response = await getLocationTree()
    if (response && response.data) {
      // 扁平化树形结构
      departmentList.value = flattenDepartmentTree(response.data)
      console.log('【心内科】已加载就诊科室列表：', departmentList.value.length, '个科室')
    }
  } catch (error) {
    console.error('【心内科】加载就诊科室列表失败：', error)
    ElMessage.warning('加载就诊科室列表失败，使用默认数据')
  }
}

// 获取选中科室的名称
const getSelectedDeptName = () => {
  if (selectedDept.value === 'all') return null
  const dept = departmentList.value.find(d => d.id === selectedDept.value)
  return dept ? dept.name : null
}

// 过滤后的智能候选池数据
const filteredCandidatePoolList = computed(() => {
  if (selectedDept.value === 'all') {
    return originalCandidatePoolList.value
  }
  const deptName = getSelectedDeptName()
  if (!deptName) return originalCandidatePoolList.value
  return originalCandidatePoolList.value.filter(item => item.room === deptName)
})

// 原始队列数据存储（用于过滤）
const originalQueueList = ref(getInitialQueueList())

const parseMmSsToSeconds = (mmss) => {
  if (!mmss || typeof mmss !== 'string') return 0
  const [mm, ss] = mmss.split(':')
  const m = Number(mm)
  const s = Number(ss)
  if (Number.isNaN(m) || Number.isNaN(s)) return 0
  return m * 60 + s
}

const formatSecondsToMmSs = (totalSeconds) => {
  const safe = Math.max(0, Math.floor(totalSeconds || 0))
  const mm = String(Math.floor(safe / 60)).padStart(2, '0')
  const ss = String(safe % 60).padStart(2, '0')
  return `${mm}:${ss}`
}

// 过滤后的智能队列数据（同时考虑科室过滤和状态过滤）
const filteredQueueList = computed(() => {
  let filtered = originalQueueList.value

  // 先过滤掉"已完成"状态的患者（无论什么情况都不显示）
  filtered = filtered.filter(item => item.status !== '已完成')

  // 再按科室过滤
  if (selectedDept.value !== 'all') {
    const deptName = getSelectedDeptName()
    if (deptName) {
      filtered = filtered.filter(item => item.room === deptName)
    }
  }

  // 再按状态过滤（只显示等待）
  if (showOnlyWaiting.value) {
    filtered = filtered.filter(item => item.status === '等待')
  }

  return filtered
})

const handleQueueRowClick = (row) => {
  selectedQueueRow.value = row
}

// 候选池选择变化
const handleCandidateSelectionChange = (selection) => {
  selectedCandidates.value = selection
}

// 获取下一个队序号
const getNextQueueOrder = () => {
  if (originalQueueList.value.length === 0) return 1
  const maxOrder = Math.max(...originalQueueList.value.map(item => item.queueOrder))
  return maxOrder + 1
}

// 重新计算队序号（保持连续）
const recalculateQueueOrders = () => {
  originalQueueList.value.forEach((item, index) => {
    item.queueOrder = index + 1
  })
}

// 加入队列（选中的候选池患者）——落库
const handleAddToQueue = async () => {
  if (selectedCandidates.value.length === 0) {
    ElMessage.warning('请先选择要加入队列的患者')
    return
  }
  
  // 记录要加入的患者 encounterId（用于后续删除）
  const toAddEncounterIds = new Set()
  selectedCandidates.value.forEach((c) => {
    if (c.encounterId != null) {
      toAddEncounterIds.add(String(c.encounterId))
    }
  })
  
  const beforeCount = originalCandidatePoolList.value.length
  console.log('【心内科】handleAddToQueue 开始：选中', selectedCandidates.value.length, '位患者，候选池当前', beforeCount, '条')
  
  try {
    const groups = new Map()
    selectedCandidates.value.forEach((c) => {
      if (!c.organizationId) return
      if (!groups.has(c.organizationId)) {
        groups.set(c.organizationId, {
          organizationId: c.organizationId,
          organizationName: c.organizationName || c.room,
          items: []
        })
      }
      groups.get(c.organizationId).items.push({
        encounterId: c.encounterId,
        patientId: c.patientId,
        patientName: c.patientName,
        healthcareName: c.appointmentType,
        practitionerName: c.doctor,
        practitionerId: c.practitionerId ?? null,
        // Bug #410：c.room 已是诊室名称
        roomNo: c.room ?? null,
        poolId: c.poolId ?? null,
        slotId: c.slotId ?? null
      })
    })

    let added = 0
    const addedEncounterIds = new Set()
    for (const [, payload] of groups) {
      try {
        const res = await addToQueue(payload)
        if (typeof res?.data === 'number') added += res.data
        // 不论后端是否判重，只要尝试加入过的 encounterId，一律从候选池移除
        payload.items.forEach(it => {
          if (it.encounterId != null) {
            addedEncounterIds.add(String(it.encounterId))
          }
        })
      } catch (err) {
        console.error('【心内科】单个分组加入队列失败：', err)
        // 即使失败，也记录 encounterId，后续尝试删除（避免重复尝试）
        payload.items.forEach(it => {
          if (it.encounterId != null) {
            addedEncounterIds.add(String(it.encounterId))
          }
        })
      }
    }

    // 立即从候选池中删除已加入的患者（无论API是否成功，只要尝试过就删除）
    // 确保 encounterId 类型一致（统一转换为字符串）
    if (addedEncounterIds.size > 0) {
      const beforeDeleteCount = originalCandidatePoolList.value.length
      originalCandidatePoolList.value = originalCandidatePoolList.value.filter(
        c => {
          const candidateId = c.encounterId != null ? String(c.encounterId) : null
          const shouldKeep = candidateId == null || !addedEncounterIds.has(candidateId)
          return shouldKeep
        }
      )
      const deletedCount = beforeDeleteCount - originalCandidatePoolList.value.length
      console.log('【心内科】从候选池删除', deletedCount, '位患者（尝试加入的', addedEncounterIds.size, '位）')
      
      // 同时清空选中状态
      selectedCandidates.value = []
    }

    // 刷新队列数据（从数据库读取最新队列）
    await loadQueueFromDb()
    totalSignedIn.value = originalCandidatePoolList.value.length
    
    const afterCount = originalCandidatePoolList.value.length
    console.log('【心内科】handleAddToQueue 完成：候选池从', beforeCount, '条减少到', afterCount, '条，成功加入', added, '位')

    if (added > 0) {
      ElMessage.success(`成功将 ${added} 位患者加入队列（已保存）`)
    } else if (addedEncounterIds.size > 0) {
      ElMessage.warning('所选患者已在队列中，已从候选池移除')
    } else {
      ElMessage.warning('加入队列失败，请重试')
    }
  } catch (e) {
    console.error('【心内科】handleAddToQueue 异常：', e)
    // 即使出错，也尝试删除已尝试加入的患者（避免重复操作）
    if (toAddEncounterIds.size > 0) {
      const beforeDeleteCount = originalCandidatePoolList.value.length
      originalCandidatePoolList.value = originalCandidatePoolList.value.filter(
        c => {
          const candidateId = c.encounterId != null ? String(c.encounterId) : null
          return candidateId == null || !toAddEncounterIds.has(candidateId)
        }
      )
      const deletedCount = beforeDeleteCount - originalCandidatePoolList.value.length
      console.log('【心内科】异常情况下从候选池删除', deletedCount, '位患者')
      totalSignedIn.value = originalCandidatePoolList.value.length
      selectedCandidates.value = []
    }
    ElMessage.error('加入队列失败（未保存）')
  }
}

// 一键加入队列（所有候选池患者）——落库
const handleAddAllToQueue = async () => {
  if (filteredCandidatePoolList.value.length === 0) {
    ElMessage.warning('候选池中没有患者')
    return
  }
  
  // 记录要加入的所有患者 encounterId（用于后续删除）
  const toAddEncounterIds = new Set()
  filteredCandidatePoolList.value.forEach((c) => {
    if (c.encounterId != null) {
      toAddEncounterIds.add(String(c.encounterId))
    }
  })
  
  const beforeCount = originalCandidatePoolList.value.length
  console.log('【心内科】handleAddAllToQueue 开始：候选池当前', beforeCount, '条，过滤后', filteredCandidatePoolList.value.length, '条')
  
  try {
    const groups = new Map()
    filteredCandidatePoolList.value.forEach((c) => {
      if (!c.organizationId) return
      if (!groups.has(c.organizationId)) {
        groups.set(c.organizationId, {
          organizationId: c.organizationId,
          organizationName: c.organizationName || c.room,
          items: []
        })
      }
      groups.get(c.organizationId).items.push({
        encounterId: c.encounterId,
        patientId: c.patientId,
        patientName: c.patientName,
        healthcareName: c.appointmentType,
        practitionerName: c.doctor,
        practitionerId: c.practitionerId ?? null,
        // Bug #410：c.room 已是诊室名称
        roomNo: c.room ?? null,
        poolId: c.poolId ?? null,
        slotId: c.slotId ?? null
      })
    })

    let added = 0
    const addedEncounterIds = new Set()
    for (const [, payload] of groups) {
      try {
        const res = await addToQueue(payload)
        if (typeof res?.data === 'number') added += res.data
        // 不论后端是否判重，只要尝试加入过的 encounterId，一律从候选池移除
        payload.items.forEach(it => {
          if (it.encounterId != null) {
            addedEncounterIds.add(String(it.encounterId))
          }
        })
      } catch (err) {
        console.error('【心内科】单个分组一键加入队列失败：', err)
        // 即使失败，也记录 encounterId，后续尝试删除（避免重复尝试）
        payload.items.forEach(it => {
          if (it.encounterId != null) {
            addedEncounterIds.add(String(it.encounterId))
          }
        })
      }
    }

    // 立即从候选池中删除已加入的患者（无论API是否成功，只要尝试过就删除）
    // 确保 encounterId 类型一致（统一转换为字符串）
    if (addedEncounterIds.size > 0) {
      const beforeDeleteCount = originalCandidatePoolList.value.length
      originalCandidatePoolList.value = originalCandidatePoolList.value.filter(
        c => {
          const candidateId = c.encounterId != null ? String(c.encounterId) : null
          const shouldKeep = candidateId == null || !addedEncounterIds.has(candidateId)
          return shouldKeep
        }
      )
      const deletedCount = beforeDeleteCount - originalCandidatePoolList.value.length
      console.log('【心内科】从候选池删除', deletedCount, '位患者（尝试加入的', addedEncounterIds.size, '位）')
      
      // 同时清空选中状态
      selectedCandidates.value = []
    }
    
    // 刷新队列数据（从数据库读取最新队列）
    await loadQueueFromDb()
    totalSignedIn.value = originalCandidatePoolList.value.length
    
    const afterCount = originalCandidatePoolList.value.length
    console.log('【心内科】handleAddAllToQueue 完成：候选池从', beforeCount, '条减少到', afterCount, '条，成功加入', added, '位')

    if (added > 0) {
      ElMessage.success(`成功将 ${added} 位患者加入队列（已保存）`)
    } else if (addedEncounterIds.size > 0) {
      ElMessage.warning('所有候选池患者已在队列中，已从候选池移除')
    } else {
      ElMessage.warning('一键加入失败，请重试')
    }
  } catch (e) {
    console.error('【心内科】handleAddAllToQueue 异常：', e)
    // 即使出错，也尝试删除已尝试加入的患者（避免重复操作）
    if (toAddEncounterIds.size > 0) {
      const beforeDeleteCount = originalCandidatePoolList.value.length
      originalCandidatePoolList.value = originalCandidatePoolList.value.filter(
        c => {
          const candidateId = c.encounterId != null ? String(c.encounterId) : null
          return candidateId == null || !toAddEncounterIds.has(candidateId)
        }
      )
      const deletedCount = beforeDeleteCount - originalCandidatePoolList.value.length
      console.log('【心内科】异常情况下从候选池删除', deletedCount, '位患者')
      totalSignedIn.value = originalCandidatePoolList.value.length
      selectedCandidates.value = []
    }
    ElMessage.error('一键加入失败（未保存）')
  }
}

// 移出队列——落库
const handleRemoveFromQueue = async () => {
  if (!selectedQueueRow.value) {
    ElMessage.warning('请先选择要移出队列的患者')
    return
  }

  try {
    const row = selectedQueueRow.value
    if (!row.id) {
      ElMessage.warning('该队列项缺少ID，无法保存删除，请先刷新页面')
      return
    }
    
    const patientName = row.patientName || '患者'
    console.log('【心内科】开始移出队列：', patientName, 'encounterId:', row.encounterId)
    
    await removeFromQueue(row.id)
    selectedQueueRow.value = null
    
    // 刷新数据（候选池和队列）
    await loadDataFromApi()
    
    console.log('【心内科】移出队列完成，患者应已重新出现在候选池中')
    ElMessage.success(`已将 ${patientName} 移出队列，患者已重新出现在候选池中（已保存）`)
  } catch (e) {
    console.error('【心内科】handleRemoveFromQueue 异常：', e)
    ElMessage.error('移出队列失败（未保存）')
  }
}

// 是否可以上移
const canMoveUp = computed(() => {
  if (!selectedQueueRow.value) return false
  const index = originalQueueList.value.findIndex(
    item => item.queueOrder === selectedQueueRow.value.queueOrder
  )
  return index > 0
})

// 是否可以下移
const canMoveDown = computed(() => {
  if (!selectedQueueRow.value) return false
  const index = originalQueueList.value.findIndex(
    item => item.queueOrder === selectedQueueRow.value.queueOrder
  )
  return index >= 0 && index < originalQueueList.value.length - 1
})

// 上移——落库
const handleMoveUp = async () => {
  if (!selectedQueueRow.value || !canMoveUp.value) return
  if (!selectedQueueRow.value.id) {
    ElMessage.warning('该队列项缺少ID，无法保存排序，请先刷新页面')
    return
  }
  try {
    await adjustQueueOrder({ id: selectedQueueRow.value.id, direction: 'up' })
    await loadDataFromApi()
    ElMessage.success('队列顺序已调整（已保存）')
  } catch (e) {
    console.error('【心内科】handleMoveUp 异常：', e)
    ElMessage.error('队列顺序调整失败（未保存）')
  }
}

// 下移——落库
const handleMoveDown = async () => {
  if (!selectedQueueRow.value || !canMoveDown.value) return
  if (!selectedQueueRow.value.id) {
    ElMessage.warning('该队列项缺少ID，无法保存排序，请先刷新页面')
    return
  }
  try {
    await adjustQueueOrder({ id: selectedQueueRow.value.id, direction: 'down' })
    await loadDataFromApi()
    ElMessage.success('队列顺序已调整（已保存）')
  } catch (e) {
    console.error('【心内科】handleMoveDown 异常：', e)
    ElMessage.error('队列顺序调整失败（未保存）')
  }
}

let waitingTimer = null
const startWaitingTimer = () => {
  if (waitingTimer) return
  waitingTimer = window.setInterval(() => {
    // 仅"等待"状态计算等待时间（基于创建时间）
    const now = new Date()
    originalQueueList.value.forEach((item) => {
      if (item.status !== '等待') return
      // 如果有创建时间，基于创建时间计算；否则累加（兼容旧数据）
      if (item.createTime) {
        try {
          const createTime = new Date(item.createTime)
          // 检查日期是否有效
          if (!isNaN(createTime.getTime())) {
            const diffSeconds = Math.floor((now - createTime) / 1000)
            item.waitingTime = formatSecondsToMmSs(Math.max(0, diffSeconds))
          }
        } catch (e) {
          // 解析失败时使用累加方式（兼容旧数据）
          const seconds = parseMmSsToSeconds(item.waitingTime)
          item.waitingTime = formatSecondsToMmSs(seconds + 1)
        }
      } else {
        // 兼容旧数据：累加方式
        const seconds = parseMmSsToSeconds(item.waitingTime)
        item.waitingTime = formatSecondsToMmSs(seconds + 1)
      }
    })
  }, 1000)
}

const stopWaitingTimer = () => {
  if (!waitingTimer) return
  window.clearInterval(waitingTimer)
  waitingTimer = null
}

// 获取行样式类名（用于高亮当前叫号）
const getRowClassName = ({ row }) => {
  const classes = []
  if (row.status === '叫号中') classes.push('calling-row')
  if (selectedQueueRow.value && row.queueOrder === selectedQueueRow.value.queueOrder) {
    classes.push('selected-row')
  }
  return classes.join(' ')
}

// 获取状态标签类型
const getStatusTagType = (status) => {
  const statusMap = {
    '等待': 'info',
    '叫号中': 'warning',
    '就诊中': 'success',
    '跳过': 'danger',
    '已完成': ''
  }
  return statusMap[status] || ''
}

// 刷新数据（候选池=门诊挂号接口，队列=数据库接口）
const handleRefresh = async () => {
  callType.value = '刷新'
  console.log('【心内科】点击了刷新按钮，开始重新加载数据（候选池+队列）')
  selectedCandidates.value = []
  await loadDataFromApi()

  // 确保等待计时器只有一个
  stopWaitingTimer()
  startWaitingTimer()

  ElMessage.success('已刷新（已从数据库恢复队列）')
}

// 退出
const handleExit = () => {
  ElMessage.info('退出功能待实现')
  // TODO: 实现退出逻辑
}

// 后台配置
const handleConfig = () => {
  // 打开配置弹窗并加载当前选中规则
  if (rules.value.length > 0) {
    editingIndex.value = 0
    Object.assign(ruleForm, rules.value[0])
  } else {
    editingIndex.value = -1
    Object.assign(ruleForm, {
      name: '',
      dept: '心内科',
      desc: '',
      priority: 10,
      weeks: ['1', '2', '3', '4', '5'],
      expr: '{}'
    })
  }
  configDialogVisible.value = true
}

// 选呼——落库
const handleSelectCall = async () => {
  if (!selectedQueueRow.value) {
    ElMessage.warning('请先在右侧队列中选择一个患者')
    return
  }

  try {
    callType.value = '选呼'
    if (!selectedQueueRow.value.id) {
      ElMessage.warning('该队列项缺少ID，无法保存选呼，请先刷新页面')
      return
    }
    
    // 检查患者状态，只有"等待"状态才能选呼
    if (selectedQueueRow.value.status !== '等待') {
      if (selectedQueueRow.value.status === '叫号中') {
        ElMessage.info('该患者已经是"叫号中"状态')
        return
      } else {
        ElMessage.warning('只能选呼"等待"状态的患者，当前患者状态为："' + selectedQueueRow.value.status + '"')
        return
      }
    }
    
    await callPatient({ id: selectedQueueRow.value.id, organizationId: selectedQueueRow.value.organizationId })
    // 只刷新队列数据，不重新加载候选池（避免候选池数据被重置）
    await loadQueueFromDb()
    const latest = originalQueueList.value.find((i) => i.id === selectedQueueRow.value.id)
    if (latest) {
      selectedQueueRow.value = latest
      // 如果有多个"叫号中"的患者，显示当前选中的这个
      currentCall.value = { number: String(latest.queueOrder), name: latest.patientName, room: latest.room }
    }
    
    // 统计当前"叫号中"的患者数量
    const callingCount = originalQueueList.value.filter(i => i.status === '叫号中').length
    if (callingCount > 1) {
      ElMessage.success(`已选呼（已保存），当前共有 ${callingCount} 位患者处于"叫号中"状态`)
    } else {
      ElMessage.success('已选呼（已保存）')
    }
  } catch (e) {
    console.error('【心内科】handleSelectCall 异常：', e)
    ElMessage.error('选呼失败（未保存）')
  }
}

// 下一患者——落库
const handleNextPatient = async () => {
  try {
    callType.value = '下一患者'
    // 关键改进：如果用户选中了患者，优先使用选中患者的ID（像 call 方法一样）
    let reqData = {}
    if (selectedQueueRow.value?.id) {
      // 如果选中了患者，直接使用其ID（后端会通过ID直接获取，不依赖查询条件）
      reqData.id = selectedQueueRow.value.id
      reqData.organizationId = selectedQueueRow.value.organizationId
    } else {
      // 如果没有选中患者，使用查询条件（兼容旧逻辑）
      let orgId = selectedDept.value !== 'all' ? selectedDept.value : undefined
      // "全科"模式：优先用"当前叫号中/第一个等待"所在科室
      if (orgId == null) {
        const calling = originalQueueList.value.find((i) => i.status === '叫号中')
        const waiting = originalQueueList.value.find((i) => i.status === '等待')
        console.log('【心内科】handleNextPatient 查找：叫号中=', calling?.patientName, '等待=', waiting?.patientName)
        orgId = calling?.organizationId ?? waiting?.organizationId
        console.log('【心内科】handleNextPatient 确定的 orgId=', orgId)
      }
      if (orgId != null) {
        reqData.organizationId = orgId
      }
    }
    // 如果还是找不到，后端会自己找全科第一个叫号中/等待的
    await nextPatient(reqData)
    selectedQueueRow.value = null
    await loadDataFromApi()
    ElMessage.success('已呼叫下一位（已保存）')
  } catch (e) {
    console.error('【心内科】handleNextPatient 异常：', e)
    // 从错误对象中提取错误消息
    const errorMsg = e?.message || e?.response?.data?.msg || '当前范围内没有"等待"的患者'
    ElMessage.warning(errorMsg)
  }
}

// 跳过——落库（当前等同于“过号重排”）
const handleSkip = async () => {
  try {
    callType.value = '跳过'
    // 关键改进：如果用户选中了患者，优先使用选中患者的ID（像 call 方法一样）
    let reqData = {}
    if (selectedQueueRow.value?.id) {
      // 如果选中了患者，直接使用其ID（后端会通过ID直接获取，不依赖查询条件）
      reqData.id = selectedQueueRow.value.id
      reqData.organizationId = selectedQueueRow.value.organizationId
    } else {
      // 如果没有选中患者，使用查询条件（兼容旧逻辑）
      let orgId = selectedDept.value !== 'all' ? selectedDept.value : undefined
      // “全科”模式：优先用“当前叫号中”所在科室
      if (orgId == null) {
        const calling = originalQueueList.value.find((i) => i.status === '叫号中')
        orgId = calling?.organizationId
      }
      if (orgId != null) {
        reqData.organizationId = orgId
      }
    }
    await skipPatient(reqData)
    selectedQueueRow.value = null
    await loadDataFromApi()
    ElMessage.success('已跳过（已保存）')
  } catch (e) {
    console.error('【心内科】handleSkip 异常：', e)
    // 从错误对象中提取错误消息
    const errorMsg = e?.message || e?.response?.data?.msg || '当前没有"叫号中"的患者'
    ElMessage.warning(errorMsg)
  }
}

// 完成——落库
const handleComplete = async () => {
  try {
    callType.value = '完成'
    // 关键改进：如果用户选中了患者，优先使用选中患者的ID（像 call 方法一样）
    let reqData = {}
    if (selectedQueueRow.value?.id) {
      // 如果选中了患者，直接使用其ID（后端会通过ID直接获取，不依赖查询条件）
      reqData.id = selectedQueueRow.value.id
      reqData.organizationId = selectedQueueRow.value.organizationId
    } else {
      // 如果没有选中患者，使用查询条件（兼容旧逻辑）
      let orgId = selectedDept.value !== 'all' ? selectedDept.value : undefined
      // “全科”模式：优先用“当前叫号中”所在科室
      if (orgId == null) {
        const calling = originalQueueList.value.find((i) => i.status === '叫号中')
        orgId = calling?.organizationId
      }
      if (orgId != null) {
        reqData.organizationId = orgId
      }
    }
    await completeCall(reqData)
    selectedQueueRow.value = null
    await loadDataFromApi()
    ElMessage.success('已完成（已保存）')
  } catch (e) {
    console.error('【心内科】handleComplete 异常：', e)
    // 从错误对象中提取错误消息
    const errorMsg = e?.message || e?.response?.data?.msg || '当前没有"叫号中"的患者'
    ElMessage.warning(errorMsg)
  }
}

// 过号重排——落库
const handleRequeue = async () => {
  try {
    callType.value = '过号重排'
    // 关键改进：如果用户选中了患者，优先使用选中患者的ID（像 call 方法一样）
    let reqData = {}
    if (selectedQueueRow.value?.id) {
      // 如果选中了患者，直接使用其ID（后端会通过ID直接获取，不依赖查询条件）
      reqData.id = selectedQueueRow.value.id
      reqData.organizationId = selectedQueueRow.value.organizationId
    } else {
      // 如果没有选中患者，使用查询条件（兼容旧逻辑）
      let orgId = selectedDept.value !== 'all' ? selectedDept.value : undefined
      // “全科”模式：优先用“当前叫号中”所在科室
      if (orgId == null) {
        const calling = originalQueueList.value.find((i) => i.status === '叫号中')
        orgId = calling?.organizationId
      }
      if (orgId != null) {
        reqData.organizationId = orgId
      }
    }
    await requeuePatient(reqData)
    // 如果执行到这里，说明操作成功
    selectedQueueRow.value = null
    await loadDataFromApi()
    ElMessage.success('已过号重排（已保存）')
  } catch (e) {
    console.error('【心内科】handleRequeue 异常：', e)
    // 从错误对象中提取错误消息
    // 注意：当后端返回 R.fail() 时，code=500，响应拦截器会 Promise.reject(new Error(msg))
    // 所以错误消息在 e.message 中
    const errorMsg = e?.message || e?.response?.data?.msg || '当前没有等待中的患者'
    ElMessage.warning(errorMsg)
    // 失败时不要刷新数据，避免显示错误的状态
    // await loadDataFromApi() // 注释掉，避免刷新数据
  }
}

// 后台配置：选择规则
const handleSelectRule = (index) => {
  editingIndex.value = index
  Object.assign(ruleForm, rules.value[index])
}

// 后台配置：保存当前规则到列表
const persistRuleForm = () => {
  if (!ruleForm.name || !ruleForm.priority) {
    ElMessage.warning('请填写规则名称和优先级')
    return false
  }
  if (!Array.isArray(ruleForm.weeks) || ruleForm.weeks.length === 0) {
    ElMessage.warning('请选择生效周几')
    return false
  }
  const parsed = tryParseExpr(ruleForm.expr)
  if (parsed === null) return false

  const target = {
    name: ruleForm.name,
    dept: ruleForm.dept || '心内科',
    desc: ruleForm.desc || '',
    priority: Number(ruleForm.priority) || 0,
    weeks: [...ruleForm.weeks],
    expr: ruleForm.expr
  }

  if (editingIndex.value >= 0 && editingIndex.value < rules.value.length) {
    rules.value.splice(editingIndex.value, 1, target)
  } else {
    rules.value.push(target)
    editingIndex.value = rules.value.length - 1
  }
  return true
}

// JSON 语法检查
const tryParseExpr = (expr) => {
  try {
    JSON.parse(expr || '{}')
    return true
  } catch (e) {
    ElMessage.error('条件表达式 JSON 语法错误')
    return null
  }
}

// 后台配置：新增规则
const handleAddRule = () => {
  const ok = persistRuleForm()
  if (!ok) return
  const newRule = {
    name: '新规则',
    dept: '心内科',
    desc: '',
    priority: rules.value.length * 10 + 10,
    weeks: ['1', '2', '3', '4', '5'],
    expr: '{}'
  }
  rules.value.push(newRule)
  editingIndex.value = rules.value.length - 1
  Object.assign(ruleForm, newRule)
}

// 后台配置：删除规则
const handleDeleteRule = (index) => {
  rules.value.splice(index, 1)
  if (rules.value.length === 0) {
    editingIndex.value = -1
    Object.assign(ruleForm, {
      name: '',
      dept: '心内科',
      desc: '',
      priority: 10,
      weeks: ['1', '2', '3', '4', '5'],
      expr: '{}'
    })
    return
  }
  const newIndex = Math.max(0, Math.min(index, rules.value.length - 1))
  editingIndex.value = newIndex
  Object.assign(ruleForm, rules.value[newIndex])
}

// 后台配置：上移
const handleRuleMoveUp = (index) => {
  if (index <= 0) return
  ;[rules.value[index - 1], rules.value[index]] = [rules.value[index], rules.value[index - 1]]
  editingIndex.value = index - 1
}

// 后台配置：下移
const handleRuleMoveDown = (index) => {
  if (index >= rules.value.length - 1) return
  ;[rules.value[index + 1], rules.value[index]] = [rules.value[index], rules.value[index + 1]]
  editingIndex.value = index + 1
}

// 后台配置：保存当前规则
const handleSaveCurrentRule = () => {
  const ok = persistRuleForm()
  if (ok) {
    ElMessage.success('当前规则已保存')
  }
}

// 后台配置：保存全部（本地模拟）
const handleSaveAllRules = () => {
  const ok = persistRuleForm()
  if (!ok) return
  // TODO: 调用后端保存接口
  ElMessage.success('全部规则已保存（前端模拟）')
}

// 后台配置：语法检查
const handleValidateRule = () => {
  const ok = tryParseExpr(ruleForm.expr)
  if (ok) {
    ElMessage.success('语法检查通过')
  }
}

// 构建条件对象（公共函数）
const buildConditions = () => {
  const conditions = {}
  
  // 年龄条件
  if (quickGeneratorForm.ageOperator && quickGeneratorForm.ageValue !== null) {
    conditions.age = `${quickGeneratorForm.ageOperator}${quickGeneratorForm.ageValue}`
  }
  
  // 号别
  if (quickGeneratorForm.regType) {
    conditions.regType = quickGeneratorForm.regType
  }
  
  // 科室
  if (quickGeneratorForm.dept) {
    conditions.dept = quickGeneratorForm.dept
  }
  
  // 医生
  if (quickGeneratorForm.doctor) {
    conditions.doctor = quickGeneratorForm.doctor
  }
  
  // 自定义条件
  if (quickGeneratorForm.customKey && quickGeneratorForm.customValue) {
    conditions[quickGeneratorForm.customKey] = quickGeneratorForm.customValue
  }
  
  return conditions
}

// 预览JSON（计算属性）
const previewJson = computed(() => {
  return JSON.stringify(buildConditions(), null, 2)
})

// 后台配置：快速生成器
const handleQuickGenerate = () => {
  // 如果当前表达式不为空，尝试解析并填充表单
  if (ruleForm.expr) {
    try {
      const parsed = JSON.parse(ruleForm.expr)
      
      // 解析年龄条件
      if (parsed.age) {
        const ageMatch = parsed.age.match(/^(>=|<=|=|>|<)(\d+)$/)
        if (ageMatch) {
          quickGeneratorForm.ageOperator = ageMatch[1]
          quickGeneratorForm.ageValue = parseInt(ageMatch[2])
        }
      }
      
      // 填充其他字段
      if (parsed.regType) quickGeneratorForm.regType = parsed.regType
      if (parsed.dept) quickGeneratorForm.dept = parsed.dept
      if (parsed.doctor) quickGeneratorForm.doctor = parsed.doctor
      
      // 填充自定义字段（排除已知字段）
      const knownFields = ['age', 'regType', 'dept', 'doctor']
      for (const key in parsed) {
        if (!knownFields.includes(key)) {
          quickGeneratorForm.customKey = key
          quickGeneratorForm.customValue = parsed[key]
          break
        }
      }
    } catch (e) {
      // 如果解析失败，清空表单
      resetQuickGeneratorForm()
    }
  } else {
    // 如果表达式为空，重置表单
    resetQuickGeneratorForm()
  }
  
  quickGeneratorDialogVisible.value = true
}

// 重置快速生成器表单
const resetQuickGeneratorForm = () => {
  quickGeneratorForm.ageOperator = null
  quickGeneratorForm.ageValue = null
  quickGeneratorForm.regType = null
  quickGeneratorForm.dept = null
  quickGeneratorForm.doctor = null
  quickGeneratorForm.customKey = ''
  quickGeneratorForm.customValue = ''
}

// 应用快速生成器生成的JSON
const handleApplyQuickGenerate = () => {
  const conditions = buildConditions()
  
  // 检查是否至少有一个条件
  if (Object.keys(conditions).length === 0) {
    ElMessage.warning('请至少设置一个条件')
    return
  }
  
  // 应用到规则表单
  ruleForm.expr = JSON.stringify(conditions, null, 2)
  
  // 关闭对话框
  quickGeneratorDialogVisible.value = false
  
  ElMessage.success('条件表达式已生成并应用')
}

// 后台配置：测试规则（占位）
const handleTestRule = () => {
  const ok = tryParseExpr(ruleForm.expr)
  if (!ok) return
  ElMessage.info('测试规则功能待接入后端，当前为占位')
}

// 组件挂载
onMounted(() => {
  // 加载就诊科室列表
  loadDepartmentList()
  // 初始化：优先从后端加载，失败则回退本地假数据
  loadDataFromApi()
  startWaitingTimer()
})

onUnmounted(() => {
  stopWaitingTimer()
})
</script>

<style lang="scss" scoped>
.smart-triage-queue {
  width: 100%;
  height: 100vh;
  display: flex;
  flex-direction: column;
  background-color: #f5f5f5;
  padding: 20px;
  box-sizing: border-box;
  overflow: hidden;

  // 顶部标题栏
  .header-section {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    background-color: #fff;
    padding: 20px;
    border-radius: 8px;
    margin-bottom: 20px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);

    .header-left {
      flex: 1;
      display: flex;
      align-items: center;

      .title {
        font-size: 24px;
        font-weight: bold;
        color: #333;
      }

      .header-info {
        display: flex;
        gap: 20px;
        margin-bottom: 10px;
        font-size: 14px;
        color: #666;

        .date-info {
          font-weight: 500;
        }

        .stat-info {
          color: #409eff;
        }
      }

      .current-call-info {
        font-size: 16px;
        color: #333;
        font-weight: 500;
      }
    }

    .header-right {
      display: flex;
      gap: 10px;
    }
  }

  // 主要内容区域
  .main-content {
    flex: 1;
    display: flex;
    gap: 20px;
    margin-bottom: 20px;
    min-height: 0;

    .left-panel,
    .right-panel {
      flex: 1;
      background-color: #fff;
      border-radius: 8px;
      box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
      display: flex;
      flex-direction: column;
      overflow: hidden;

      .panel-header {
        padding: 15px 20px;
        border-bottom: 2px solid #409eff;
        background-color: #f8f9fa;

        .panel-title {
          font-size: 16px;
          font-weight: bold;
          color: #333;
        }
      }

      .table-container {
        flex: 1;
        overflow: hidden;
        padding: 10px;
      }

      .display-options {
        padding: 10px 20px;
        border-top: 1px solid #ebeef5;
        display: flex;
        gap: 10px;
        justify-content: space-between;
        align-items: center;

        .queue-actions-left {
          display: flex;
          gap: 10px;
        }

        .queue-actions-right {
          display: flex;
          gap: 10px;
        }
      }

      .candidate-actions {
        padding: 10px 20px;
        border-top: 1px solid #ebeef5;
        display: flex;
        gap: 10px;
        justify-content: flex-start;
      }
    }
  }

  // 底部控制面板
  .footer-section {
    background-color: #fff;
    border-radius: 8px;
    padding: 20px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);

    .filter-section {
      margin-bottom: 20px;

      .filter-label {
        font-size: 14px;
        font-weight: bold;
        color: #333;
        margin-bottom: 10px;
      }

      .filter-select-wrapper {
        width: 100%;
      }
    }

    .call-control-section {
      margin-bottom: 20px;
      padding: 15px;
      background-color: #f8f9fa;
      border-radius: 6px;

      .call-control-label {
        font-size: 14px;
        font-weight: bold;
        color: #333;
        margin-bottom: 10px;
      }

      .call-control-content {
        display: flex;
        justify-content: space-between;
        align-items: center;

        .current-call-display {
          font-size: 16px;
          color: #333;
          font-weight: 500;
        }

        .control-buttons {
          display: flex;
          gap: 10px;
        }
      }
    }

    .led-section {
      margin-bottom: 15px;
      padding: 15px;
      background-color: #1a1a1a;
      border-radius: 6px;
      display: flex;
      align-items: center;
      gap: 10px;

      .led-label {
        font-size: 14px;
        color: #fff;
        font-weight: bold;
      }

      .led-display {
        flex: 1;
        font-size: 20px;
        color: #00ff00;
        font-weight: bold;
        font-family: 'Courier New', monospace;
        text-shadow: 0 0 10px rgba(0, 255, 0, 0.5);
      }
    }

    .voice-section {
      display: flex;
      align-items: center;
      gap: 10px;
      padding: 10px;
      background-color: #f0f0f0;
      border-radius: 6px;

      .voice-label {
        font-size: 14px;
        color: #666;
        font-weight: bold;
      }

      .voice-text {
        font-size: 14px;
        color: #333;
      }
    }

// 配置弹窗样式
.config-container {
  display: flex;
  gap: 20px;
  min-height: 500px;
}

.config-topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 0 12px 0;
}

.config-topbar-actions {
  display: flex;
  gap: 12px;
}

.config-dialog-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
}

.config-dialog-title {
  font-size: 18px;
  font-weight: 600;
  color: #333;
}

.config-left {
  width: 30%;
  background: #f8f9fb;
  border: 1px solid #ebeef5;
  border-radius: 6px;
  padding: 12px;
  box-sizing: border-box;
}

.rule-card {
  border: 1px solid #dcdfe6;
  border-radius: 6px;
  padding: 14px 14px 12px;
  margin: 0 6px 14px;
  background: #fff;
  cursor: pointer;
  box-shadow: 0 1px 6px rgba(0, 0, 0, 0.06);
  position: relative;
}

.rule-card.active {
  border-color: #409eff;
  box-shadow: 0 0 0 2px #409eff22;
}

.rule-card.active::before {
  content: '';
  position: absolute;
  left: 0;
  top: 10px;
  bottom: 10px;
  width: 4px;
  background: #409eff;
  border-radius: 0 4px 4px 0;
}

.rule-card:hover {
  border-color: #b3d8ff;
}

.rule-title {
  font-weight: 600;
  margin-bottom: 4px;
}

.rule-sub {
  font-size: 12px;
  color: #666;
  margin-bottom: 4px;
}

.rule-actions {
  display: flex;
  gap: 6px;
  margin-top: 6px;
  padding-top: 8px;
  border-top: 1px dashed #ebeef5;
}

.config-right {
  flex: 1;
  border: 1px solid #ebeef5;
  border-radius: 6px;
  padding: 16px;
  box-sizing: border-box;
  background: #fff;
}

.config-form-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 12px;
  font-weight: 600;
}

.config-form-buttons {
  display: flex;
  gap: 8px;
}

.config-inline-actions {
  display: flex;
  gap: 12px;
  padding-left: 110px; // 对齐 label-width
  margin-top: -6px;
}

.config-footer {
  display: flex;
  justify-content: flex-end;
  gap: 12px;
}

.config-fullwidth {
  width: 100%;
}
  }
}

// 表格行样式
:deep(.calling-row) {
  background-color: #fffbe6 !important;
}

:deep(.selected-row) {
  background-color: #e8f4ff !important;
}

:deep(.el-table) {
  .el-table__body {
    tr.calling-row {
      background-color: #fffbe6;
    }
    tr.selected-row {
      background-color: #e8f4ff;
    }
  }
}
</style>
