<template>
    <div class="tcm-diagnosis">
        <el-card class="filter-card !border-none" shadow="never">
            <!-- 第一行：日期 + 患者搜索 -->
            <div class="filter-main">
                <div class="date-chips">
                    <span
                        v-for="tab in dateTabs"
                        :key="tab.value"
                        :class="['chip', { active: formData.appointment_date === tab.value }]"
                        @click="handleDateTabClick(tab.value)"
                    >
                        {{ tab.label }}
                        <em v-if="tab.value && dateCounts[tab.value] !== undefined">{{ dateCounts[tab.value] }}</em>
                    </span>
                </div>
                <div class="search-row">
                    <el-input v-model="formData.patient_name" placeholder="患者姓名" clearable class="search-input" @keyup.enter="doSearch" />
                    <el-button type="primary" @click="doSearch">查询</el-button>
                </div>
            </div>
            <!-- 第二行：挂号/确认 一键筛选，无需下拉 -->
            <div class="filter-chips">
                <span class="chip-label">挂号：</span>
                <span
                    v-for="opt in hasAptOptions"
                    :key="opt.value"
                    :class="['chip', 'chip-sm', { active: formData.has_appointment === opt.value }]"
                    @click="setHasAppointment(opt.value)"
                >
                    {{ opt.label }}
                </span>
                <span class="chip-divider" />
                <span class="chip-label">确认：</span>
                <span
                    v-for="opt in confirmedOptions"
                    :key="opt.value"
                    :class="['chip', 'chip-sm', { active: formData.diagnosis_confirmed === opt.value }]"
                    @click="setConfirmed(opt.value)"
                >
                    {{ opt.label }}
                </span>
                <el-button link type="primary" size="small" class="more-btn" @click="showMoreFilter = !showMoreFilter">
                    {{ showMoreFilter ? '收起' : '更多筛选' }}
                    <el-icon :class="{ 'rotate-180': showMoreFilter }"><ArrowDown /></el-icon>
                </el-button>
            </div>
            <!-- 更多筛选（折叠） -->
            <div v-show="showMoreFilter" class="filter-more">
                <el-select
                    v-model="formData.assistant_dept_id"
                    placeholder="医助部门"
                    clearable
                    filterable
                    size="small"
                    class="filter-dept-select"
                    @change="onAssistantDeptChange"
                >
                    <el-option v-for="d in departmentFlatOptions" :key="d.id" :label="d.label" :value="d.id" />
                </el-select>
                <el-select v-model="formData.diagnosis_type" placeholder="诊断类型" clearable size="small" @change="doSearch">
                    <el-option v-for="item in diagnosisTypeOptions" :key="item.value" :label="item.name" :value="item.value" />
                </el-select>
                <el-select v-model="formData.syndrome_type" placeholder="证型" clearable size="small" @change="doSearch">
                    <el-option v-for="item in syndromeTypeOptions" :key="item.value" :label="item.name" :value="item.value" />
                </el-select>
                <el-select v-model="formData.assistant_id" placeholder="医助" clearable filterable size="small" @change="doSearch">
                    <el-option v-for="item in filteredAssistantOptions" :key="item.id" :label="item.name" :value="Number(item.id)" />
                </el-select>
                <el-button size="small" @click="handleReset">重置</el-button>
            </div>
        </el-card>

        <el-card class="!border-none mt-3 list-card" shadow="never">
            <div class="list-header">
                <div class="list-actions">
                    <el-button type="primary" @click="handleAdd" v-perms="['tcm.diagnosis/add']">新增诊单</el-button>
                    <el-button
                        v-perms="['tcm.diagnosis/assign']"
                        type="success"
                        @click="handleBatchAssign"
                        :disabled="selectedIds.length === 0"
                    >
                        批量指派医助
                    </el-button>
                </div>
                <div v-if="selectedIds.length > 0" class="list-selected">已选 {{ selectedIds.length }} 条</div>
            </div>
            <div class="table-wrap px-4">
            <el-table 
                :data="pager.lists" 
                size="default"
                v-loading="pager.loading"
                @selection-change="handleSelectionChange"
                :row-class-name="getRowClassName"
                class="diagnosis-table"
                stripe
            >
                <el-table-column type="selection" width="48" align="center" />
                <el-table-column label="ID" min-width="70">
                    <template #default="{ row }">
                        <div class="patient-cell">
                            <div class="patient-info">
                                <div class="patient-name">
                                    {{ row.id }}
                                    <el-tag v-if="row.assign_read_at === null && row.assistant_id > 0" type="danger" size="small" effect="dark" class="ml-1">NEW</el-tag>
                                </div>
                            </div>
                        </div>
                    </template>
                </el-table-column>
                <el-table-column label="患者" min-width="60">
                    <template #default="{ row }">
                        <div class="patient-cell">
                            <!-- <div class="patient-avatar">{{ (row.patient_name || '患').charAt(0) }}</div> -->
                            <div class="patient-info">
                                <div class="patient-name">{{ row.patient_name }}</div>
                            </div>
                        </div>
                    </template>
                </el-table-column>
                <el-table-column label=" 性别 / 年龄" width="100" align="left">
                    <template #default="{ row }">
                        <div >{{ row.gender_desc || '-' }} · {{ row.age ?? '-' }}岁</div>
                    </template>
                </el-table-column>
                <el-table-column label="挂号" min-width="180">
                    <template #default="{ row }">
                        <div
                            class="appointment-cell"
                            :class="appointmentCellClasses(row)"
                        >
                            <template v-if="row.has_appointment">
                                <div class="apt-badge">{{ appointmentStatusLabel(row) }}</div>
                                <div class="apt-doctor">{{ row.appointment_doctor_name || '-' }}</div>
                                <div class="apt-time">{{ row.appointment_time_text || '-' }}</div>
                            </template>
                            <span v-else class="apt-none">未挂号</span>
                        </div>
                    </template>
                </el-table-column>
                <el-table-column label="确认" width="88" align="center">
                    <template #default="{ row }">
                        <span v-if="isDiagnosisConfirmed(row)" class="status-confirmed">已确认</span>
                        <span v-else class="status-unconfirmed">未确认</span>
                    </template>
                </el-table-column>
                <el-table-column label="复诊" min-width="150">
                    <template #default="{ row }">
                        <div v-if="row.has_prescription" class="followup-cell">
                            <div class="followup-time">{{ row.followup_time_text || '—' }}</div>
                            <div class="followup-doctor">{{ row.followup_doctor_name || '—' }}</div>
                            <div
                                v-if="row.followup_prescription_count > 0"
                                class="followup-count"
                            >
                                第{{ row.followup_prescription_count }}次复诊
                            </div>
                            <el-tag
                                v-if="row.followup_rx_voided"
                                type="info"
                                size="small"
                                effect="plain"
                                class="followup-void-tag"
                            >
                                处方已作废
                            </el-tag>
                        </div>
                        <span v-else class="followup-none">无</span>
                    </template>
                </el-table-column>
                <el-table-column label="助理" min-width="120">
                    <template #default="{ row }">
                        <el-tooltip
                            :disabled="!assistantCellTooltip(row)"
                            :content="assistantCellTooltip(row)"
                            placement="top"
                        >
                            <div class="assistant-cell">
                                <div class="assistant-name">{{ getAssistantName(row.assistant_id || row.assistant) }}</div>
                                <div v-if="getAssistantDeptNames(row.assistant_id || row.assistant)" class="assistant-dept">
                                    {{ getAssistantDeptNames(row.assistant_id || row.assistant) }}
                                </div>
                            </div>
                        </el-tooltip>
                    </template>
                </el-table-column>
                <el-table-column label="开方" min-width="96" align="center">
                    <template #default="{ row }">
                        <div v-if="row.has_prescription" class="prescription-status-cell">
                            <span class="status-prescribed">已开方</span>
                            <span class="prescription-times">（{{ row.followup_prescription_count || 1 }}次）</span>
                        </div>
                        <span v-else class="status-unprescribed">未开方</span>
                    </template>
                </el-table-column>
                <el-table-column label="视频旁观" width="120" fixed="right" align="center">
                    <template #default="{ row }">
                        <div class="video-watch-col">
                            <template v-if="canWatchCallEntry(row)">
                                <template v-if="watchCallShowEnterButton(row)">
                                    <el-tooltip :content="watchCallEnterTooltip(row)" placement="top">
                                        <span class="video-watch-trigger">
                                            <el-button
                                                type="primary"
                                                link
                                                size="small"
                                                @click="onWatchCallEntryClick(row)"
                                            >
                                                进入旁观
                                            </el-button>
                                        </span>
                                    </el-tooltip>
                                </template>
                                <span v-else class="video-watch-muted">{{ watchCallAssistantIdleText(row) }}</span>
                            </template>
                            <span v-else class="video-watch-muted">{{ watchCallPublicStatus(row) }}</span>
                        </div>
                    </template>
                </el-table-column>
                <el-table-column label="操作" width="260" fixed="right" align="left">
                    <template #default="{ row }">
                        <div class="action-cell">
                            <el-button type="primary" link size="small" @click="handleEdit(row.id)" v-perms="['tcm.diagnosis/edit']">诊单</el-button>
                            <el-button 
                                v-perms="['tcm.diagnosis/guahao']"
                                type="success" 
                                link 
                                size="small"
                                @click="handleAppointment(row)"
                            >预约</el-button>
                            <el-button 
                                v-perms="['tcm.diagnosis/edit']"
                                type="warning" 
                                link 
                                size="small"
                                @click="handleFillIdCard(row)"
                            >补全身份证</el-button>
                            <el-dropdown trigger="click" @command="(cmd) => handleRowAction(cmd, row)" placement="bottom-end">
                                <el-button type="info" link size="small">更多<el-icon class="el-icon--right"><ArrowDown /></el-icon></el-button>
                                <template #dropdown>
                                    <el-dropdown-menu>
                                        <el-dropdown-item v-if="hasPermission(['tcm.diagnosis/assign'])" command="assign"><el-icon><User /></el-icon>指派</el-dropdown-item>
                                        <el-dropdown-item v-if="isAppointmentActiveForVideo(row) && hasPermission(['tcm.diagnosis/videoQr'])" command="videoQr"><el-icon><Picture /></el-icon>视频二维码</el-dropdown-item>
                                        <el-dropdown-item v-if="hasPermission(['tcm.diagnosis/guahao']) && isAppointmentActiveForVideo(row)" command="confirmQr"><el-icon><Picture /></el-icon>二维码</el-dropdown-item>
                                        <el-dropdown-item v-if="canCancelAppointmentRow(row) && hasPermission(['tcm.diagnosis/guahao'])" command="cancelApt"><el-icon><CircleClose /></el-icon>取消挂号</el-dropdown-item>
                                        <el-dropdown-item command="order" v-if="hasPermission(['tcm.diagnosis/order'])"><el-icon><Document /></el-icon>创建订单</el-dropdown-item>
                                        <el-dropdown-item v-if="hasPermission(['tcm.diagnosis/delete'])" command="delete" divided><el-icon><Delete /></el-icon><span class="text-danger">删除</span></el-dropdown-item>
                                    </el-dropdown-menu>
                                </template>
                            </el-dropdown>
                        </div>
                    </template>
                </el-table-column>
            </el-table>
            <div class="pagination-wrap">
                <pagination v-model="pager" @change="getLists" />
            </div>
            </div>
        </el-card>

        <edit-popup ref="editRef" @success="refreshListAfterPopupSave" />
        <detail-popup ref="detailRef" />
        <appointment-popup ref="appointmentRef" @success="refreshListAfterPopupSave" />
        <assistant-watch-call-dialog
            v-model="watchCallVisible"
            :diagnosis-id="watchCallDiagnosisId"
            @closed="getLists"
        />
        
        <!-- 小程序二维码弹窗 -->
        <el-dialog
            v-model="qrcodeDialogVisible"
            :title="qrcodeDialogTitle"
            width="400px"
            :close-on-click-modal="false"
        >
            <div class="flex flex-col items-center justify-center py-4">
                <div v-if="qrcodeLoading" class="text-center">
                    <el-icon class="is-loading" :size="40">
                        <Loading />
                    </el-icon>
                    <div class="mt-4 text-gray-500">生成中...</div>
                </div>
                <div v-else-if="qrcodeUrl" class="text-center">
                    <img :src="qrcodeUrl" alt="小程序二维码" class="w-64 h-64 border border-gray-200 rounded" />
                    <div class="mt-4 text-sm text-gray-600">
                        <div>患者：{{ currentQRCodePatient?.patient_name }}</div>
                        <div class="mt-2">挂号时间：{{ qrcodeAppointmentTimeText }}</div>
                        <div class="mt-2">请使用企业微信扫描二维码，然后转发给患者</div>
                    </div>
                </div>
                <div v-else class="text-center text-gray-500">
                    生成失败，请重试
                </div>
            </div>
            <template #footer>
                <el-button @click="qrcodeDialogVisible = false">关闭</el-button>
                <el-button v-if="!qrcodeLoading" type="primary" @click="handleRegenerateQRCode">
                    重新生成
                </el-button>
            </template>
        </el-dialog>
        
        <!-- 批量指派医助弹窗 -->
        <el-dialog
            v-model="assignDialogVisible"
            :title="assignMode === 'batch' ? '批量指派医助' : '指派医助'"
            width="500px"
        >
            <el-form :model="assignForm" label-width="100px">
                <el-form-item label="选择医助" required>
                    <el-select
                        v-model="assignForm.assistant_id"
                        placeholder="请选择医助"
                        class="w-full"
                        filterable
                        clearable
                        value-key="id"
                    >
                        <el-option
                            v-for="item in assistantOptions"
                            :key="item.id"
                            :label="item.name"
                            :value="Number(item.id)"
                        />
                    </el-select>
                </el-form-item>
                <el-form-item label="继承">
                    <el-checkbox v-model="assignForm.is_inherit">继承</el-checkbox>
                    <span class="text-gray-400 text-xs ml-2">勾选后写入指派记录</span>
                </el-form-item>
                <el-form-item v-if="assignMode === 'batch'" label="已选择">
                    <div class="text-gray-500">{{ selectedIds.length }} 条诊单记录</div>
                </el-form-item>
                <el-form-item v-else label="诊单信息">
                    <div class="text-gray-500">
                        <div>患者：{{ currentRow?.patient_name }}</div>
                        <div>诊断日期：{{ currentRow?.diagnosis_date_text }}</div>
                    </div>
                </el-form-item>
            </el-form>
            <template #footer>
                <el-button @click="assignDialogVisible = false">取消</el-button>
                <el-button 
                    type="primary" 
                    @click="handleConfirmAssign"
                    :loading="assignLoading"
                >
                    确定指派
                </el-button>
            </template>
        </el-dialog>

        <!-- 创建订单弹窗 -->
        <el-dialog
            v-model="createOrderVisible"
            title="创建订单"
            width="600px"
            @close="resetOrderForm"
        >
            <el-form
                ref="orderFormRef"
                :model="orderForm"
                :rules="orderRules"
                label-width="100px"
            >
                <el-form-item label="患者">
                    <span class="font-medium">{{ orderForm.patient_name }} ({{ orderForm.phone }})</span>
                </el-form-item>

                <el-form-item label="订单类型" prop="order_type">
                    <el-select v-model="orderForm.order_type" placeholder="请选择" class="w-full">
                        <el-option label="挂号费" :value="1" />
                        <el-option label="问诊费" :value="2" />
                        <el-option label="药品费用" :value="3" />
                        <el-option label="首付费用" :value="4" />
                        <el-option label="尾款费用" :value="5" />
                        <el-option label="其他费用" :value="6" />
                        <el-option label="全部费用" :value="7" />
                        <el-option label="驼奶费用" :value="8" />
                    </el-select>
                </el-form-item>

                <el-form-item label="订单金额" prop="amount">
                    <el-input-number
                        v-model="orderForm.amount"
                        :min="0"
                        :step="0.01"
                        :precision="2"
                        placeholder="请输入金额"
                        class="w-full"
                    />
                </el-form-item>

                <el-form-item label="备注" prop="remark">
                    <el-input
                        v-model="orderForm.remark"
                        type="textarea"
                        :rows="3"
                        placeholder="请输入备注（可选）"
                    />
                </el-form-item>
            </el-form>
            <template #footer>
                <el-button @click="createOrderVisible = false">取消</el-button>
                <el-button type="primary" @click="submitOrder" :loading="orderLoading">创建订单</el-button>
            </template>
        </el-dialog>

        <!-- 补全身份证弹窗 -->
        <el-dialog
            v-model="fillIdCardVisible"
            title="补全身份证号"
            width="400px"
            :close-on-click-modal="false"
            @close="fillIdCardForm.id_card = ''"
        >
            <el-form ref="fillIdCardFormRef" :model="fillIdCardForm" :rules="fillIdCardRules" label-width="80px">
                <el-form-item label="患者">
                    <span class="font-medium">{{ fillIdCardForm.patient_name }}</span>
                </el-form-item>
                <el-form-item label="身份证号" prop="id_card">
                    <el-input
                        v-model="fillIdCardForm.id_card"
                        placeholder="请输入15或18位身份证号"
                        maxlength="18"
                        show-word-limit
                        clearable
                    />
                </el-form-item>
            </el-form>
            <template #footer>
                <el-button @click="fillIdCardVisible = false">取消</el-button>
                <el-button type="primary" @click="submitFillIdCard" :loading="fillIdCardLoading">提交（自动更新年龄）</el-button>
            </template>
        </el-dialog>

        <!-- 企业微信聊天记录抽屉 -->
        <el-drawer
            v-model="wechatDrawerVisible"
            :title="`企业微信记录 - ${wechatCurrentPatient?.patient_name || ''}`"
            size="600px"
            :destroy-on-close="true"
        >
            <!-- 患者与企微信息 -->
            <div v-if="wechatContactInfo" class="mb-4">
                <el-descriptions :column="2" border size="small">
                    <el-descriptions-item label="患者姓名">{{ wechatCurrentPatient?.patient_name }}</el-descriptions-item>
                    <el-descriptions-item label="手机号">{{ wechatContactInfo.phone || '-' }}</el-descriptions-item>
                    <el-descriptions-item label="企微联系人ID">
                        <span v-if="wechatContactInfo.external_userid">{{ wechatContactInfo.external_userid }}</span>
                        <el-tag v-else type="info" size="small">未关联</el-tag>
                    </el-descriptions-item>
                    <el-descriptions-item label="会话存档">
                        <el-tag v-if="wechatContactInfo.msgaudit_enabled" type="success" size="small">已开通</el-tag>
                        <el-tag v-else type="info" size="small">未配置</el-tag>
                    </el-descriptions-item>
                </el-descriptions>
                <!-- 存档成员列表 -->
                <div v-if="wechatContactInfo.permit_users?.length" class="mt-2">
                    <span class="text-sm text-gray-500 mr-2">存档成员：</span>
                    <el-tag
                        v-for="uid in wechatContactInfo.permit_users"
                        :key="uid"
                        size="small"
                        class="mr-1"
                    >
                        {{ uid }}
                    </el-tag>
                </div>
                <!-- 同意存档情况 -->
                <div v-if="wechatContactInfo.msgaudit_agree?.length" class="mt-2">
                    <span class="text-sm text-gray-500 mr-2">存档同意：</span>
                    <el-tag
                        v-for="(info, idx) in wechatContactInfo.msgaudit_agree"
                        :key="idx"
                        :type="info.agree_status === 'Agree' ? 'success' : 'warning'"
                        size="small"
                        class="mr-1"
                    >
                        {{ info.userid }}: {{ info.agree_status === 'Agree' ? '已同意' : info.agree_status }}
                    </el-tag>
                </div>
            </div>

            <el-divider />

            <!-- 添加记录 -->
            <div class="mb-4">
                <div class="flex gap-2">
                    <el-input
                        v-model="wechatNewNote"
                        type="textarea"
                        :rows="2"
                        placeholder="添加沟通记录..."
                        class="flex-1"
                    />
                    <el-button type="primary" @click="handleAddRecord" :loading="wechatAddLoading" class="self-end">
                        添加
                    </el-button>
                </div>
            </div>

            <!-- 记录列表 -->
            <div v-loading="wechatRecordsLoading">
                <div v-if="wechatRecords.length === 0" class="text-center text-gray-400 py-10">
                    暂无聊天记录
                </div>
                <div v-else class="record-list">
                    <div
                        v-for="record in wechatRecords"
                        :key="record.id"
                        class="record-item"
                    >
                        <div class="record-header">
                            <div class="flex items-center gap-2">
                                <el-tag :type="record.direction === 0 ? 'primary' : 'success'" size="small">
                                    {{ record.direction === 0 ? '员工' : '客户' }}
                                </el-tag>
                                <span class="font-medium text-sm">
                                    {{ record.direction === 0 ? record.staff_name : record.external_name || wechatCurrentPatient?.patient_name }}
                                </span>
                                <el-tag v-if="record.msg_type === 'note'" type="warning" size="small">备注</el-tag>
                            </div>
                            <div class="flex items-center gap-2">
                                <span class="text-xs text-gray-400">{{ formatTimestamp(record.chat_time) }}</span>
                                <el-button type="danger" link size="small" @click="handleDeleteRecord(record.id)">
                                    删除
                                </el-button>
                            </div>
                        </div>
                        <div class="record-content">{{ record.content }}</div>
                        <div v-if="record.media_url" class="mt-1">
                            <el-image :src="record.media_url" style="max-width: 200px" fit="contain" :preview-src-list="[record.media_url]" />
                        </div>
                    </div>
                </div>

                <div v-if="wechatRecordTotal > wechatRecords.length" class="text-center mt-4">
                    <el-button link type="primary" @click="loadMoreRecords">加载更多</el-button>
                </div>
            </div>
        </el-drawer>
    </div>
</template>

<script setup lang="ts" name="tcmDiagnosis">
import { tcmDiagnosisLists, tcmDiagnosisDelete, tcmDiagnosisAssign, getAssistants, generateMiniProgramQrcode, generateOrderQrcode, getWechatChatRecords, addWechatChatRecord, deleteWechatChatRecord, getWechatExternalContact, fillIdCard } from '@/api/tcm'
import { deptAll } from '@/api/org/department'
import { orderCreate } from '@/api/order'
import { cancelAppointment } from '@/api/doctor'
import { usePaging } from '@/hooks/usePaging'
import { getDictData } from '@/api/app'
import { getWeappConfig } from '@/api/channel/weapp'
import feedback from '@/utils/feedback'
import { hasPermission } from '@/utils/perm'
import { Loading, Warning, List, CircleCheck, Clock, ArrowDown, Picture, User, Document, Delete, CircleClose } from '@element-plus/icons-vue'
import useUserStore from '@/stores/modules/user'
import { useRoute } from 'vue-router'
import { computed, defineAsyncComponent, onMounted, onUnmounted, watch } from 'vue'
import dayjs from 'dayjs'

const EditPopup = defineAsyncComponent(() => import('../diagnosis/edit.vue'))
const DetailPopup = defineAsyncComponent(() => import('../diagnosis/detail.vue'))
const AppointmentPopup = defineAsyncComponent(() => import('../diagnosis/appointment.vue'))
const AssistantWatchCallDialog = defineAsyncComponent(() => import('../diagnosis/components/AssistantWatchCallDialog.vue'))

// 格式化时间戳为日期时间
const formatDateTime = (timestamp: number | string) => {
    if (!timestamp) return '-'
    // 如果是秒级时间戳（10位），转换为毫秒级
    const ts = String(timestamp).length === 10 ? Number(timestamp) * 1000 : Number(timestamp)
    return dayjs(ts).format('YYYY-MM-DD HH:mm:ss')
}

/** 固定传给列表接口：本页只展示已开方（复诊）患者；问诊列表不传此项 */
const FOLLOW_LIST_PARAMS = { only_has_prescription: '1' as const }

const formData = reactive({
    patient_name: '',
    diagnosis_type: '',
    syndrome_type: '',
    /** 医助所属部门（与列表参数 assistant_dept_id 对应） */
    assistant_dept_id: '' as number | '',
    assistant_id: '',
    start_time: '',
    end_time: '',
    diagnosis_confirmed: '' as '' | '0' | '1',
    appointment_date: '' as string,
    has_appointment: '' as '' | '0' | '1',
    ...FOLLOW_LIST_PARAMS
})

const activeTab = ref('all')
const showMoreFilter = ref(false)

const hasAptOptions = [
    { label: '全部', value: '' },
    { label: '已挂号', value: '1' },
    { label: '未挂号', value: '0' }
]
const confirmedOptions = [
    { label: '全部', value: '' },
    { label: '已确认', value: '1' },
    { label: '未确认', value: '0' }
]

const setHasAppointment = (v: string) => {
    formData.has_appointment = v as '' | '0' | '1'
    pager.page = 1
    getLists()
    fetchDateCounts()
}

const setConfirmed = (v: string) => {
    formData.diagnosis_confirmed = v as '' | '0' | '1'
    pager.page = 1
    getLists()
    fetchDateCounts()
}

const doSearch = () => {
    pager.page = 1
    getLists()
    fetchDateCounts()
}

// 重点：当天/明天/后天挂号 Tab
const dateTabs = computed(() => [
    { label: '当天挂号', value: todayStr.value },
    { label: '明天挂号', value: tomorrowStr.value },
    { label: '后天挂号', value: dayAfterStr.value },
    { label: '全部', value: '' }
])

const dateCounts = ref<Record<string, number>>({})

const handleDateTabClick = async (value: string) => {
    formData.appointment_date = value
    pager.page = 1
    await getLists()
    fetchDateCounts()
}

/** 与主列表一致的筛选（用于日期角标统计） */
const followListDateCountParams = (appointmentDate: string) => ({
    ...FOLLOW_LIST_PARAMS,
    patient_name: formData.patient_name,
    diagnosis_type: formData.diagnosis_type,
    syndrome_type: formData.syndrome_type,
    diagnosis_confirmed: formData.diagnosis_confirmed,
    has_appointment: formData.has_appointment,
    assistant_dept_id: formData.assistant_dept_id,
    assistant_id: formData.assistant_id,
    appointment_date: appointmentDate,
    page_no: 1,
    page_size: 1
})

// 获取各日期挂号数量
const fetchDateCounts = async () => {
    try {
        const [today, tomorrow, dayAfter] = await Promise.all([
            tcmDiagnosisLists(followListDateCountParams(todayStr.value)),
            tcmDiagnosisLists(followListDateCountParams(tomorrowStr.value)),
            tcmDiagnosisLists(followListDateCountParams(dayAfterStr.value))
        ])
        dateCounts.value = {
            [todayStr.value]: today?.count ?? 0,
            [tomorrowStr.value]: tomorrow?.count ?? 0,
            [dayAfterStr.value]: dayAfter?.count ?? 0
        }
    } catch (e) {
        console.error('获取日期数量失败', e)
    }
}

/** 首屏优先主列表，错峰拉各日 tab 条数，减轻与 lists 并发争抢 */
function scheduleDeferredDateCounts() {
    const run = () => fetchDateCounts()
    if (typeof requestIdleCallback === 'function') {
        requestIdleCallback(run, { timeout: 2500 })
    } else {
        setTimeout(run, 120)
    }
}

const SILENT_LIST_POLL_MS = 10_000
let silentListPollTimer: ReturnType<typeof setInterval> | null = null

/** 定时静默刷新：不打断表格 loading，标签页隐藏时不请求 */
function silentRefreshListsAndDateCounts() {
    if (typeof document !== 'undefined' && document.hidden) return
    void getLists({ silent: true }).catch(() => {})
    void fetchDateCounts()
}

// 日期快捷选项（computed 保证每日刷新）
const pad = (n: number) => String(n).padStart(2, '0')
const todayStr = computed(() => {
    const t = new Date()
    return `${t.getFullYear()}-${pad(t.getMonth() + 1)}-${pad(t.getDate())}`
})
const tomorrowStr = computed(() => {
    const t = new Date()
    t.setDate(t.getDate() + 1)
    return `${t.getFullYear()}-${pad(t.getMonth() + 1)}-${pad(t.getDate())}`
})
const dayAfterStr = computed(() => {
    const t = new Date()
    t.setDate(t.getDate() + 2)
    return `${t.getFullYear()}-${pad(t.getMonth() + 1)}-${pad(t.getDate())}`
})

// Tab 切换
const handleTabChange = (tabName: string | number) => {
    formData.diagnosis_confirmed = ''
    formData.has_appointment = ''
    formData.appointment_date = ''
    if (tabName === 'unconfirmed') {
        formData.diagnosis_confirmed = '0'
    } else if (tabName === 'confirmed') {
        formData.diagnosis_confirmed = '1'
    } else if (tabName === 'no_appointment') {
        formData.has_appointment = '0'
    }
    pager.page = 1
    getLists()
}

// 更多下拉
const handleMoreCommand = (cmd: string) => {
    if (cmd === 'reset') handleReset()
}

// 获取字典选项
const diagnosisTypeOptions = ref<any[]>([])
const syndromeTypeOptions = ref<any[]>([])
const assistantOptions = ref<any[]>([])
/** 部门树拍平后的下拉（带层级路径） */
const departmentFlatOptions = ref<{ id: number; label: string }[]>([])

function flattenDeptTree(nodes: any[], prefix = '', out: { id: number; label: string }[] = []): { id: number; label: string }[] {
    for (const n of nodes || []) {
        if (n == null) continue
        const id = Number(n.id)
        if (!Number.isFinite(id)) continue
        const label = prefix ? `${prefix} / ${n.name}` : String(n.name ?? '')
        out.push({ id, label })
        if (Array.isArray(n.children) && n.children.length) {
            flattenDeptTree(n.children, label, out)
        }
    }
    return out
}

/** 选部门后医助下拉只显示该部门下的医助 */
const filteredAssistantOptions = computed(() => {
    const deptRaw = formData.assistant_dept_id
    if (deptRaw === '' || deptRaw == null) {
        return assistantOptions.value
    }
    const deptId = Number(deptRaw)
    if (!Number.isFinite(deptId)) {
        return assistantOptions.value
    }
    return assistantOptions.value.filter((a: any) => {
        const ids = a.dept_ids
        if (!Array.isArray(ids) || ids.length === 0) return false
        return ids.map(Number).includes(deptId)
    })
})

const onAssistantDeptChange = () => {
    const cur = formData.assistant_id
    if (cur !== '' && cur != null) {
        const num = Number(cur)
        const ok = filteredAssistantOptions.value.some((item: any) => Number(item.id) === num)
        if (!ok) {
            formData.assistant_id = ''
        }
    }
    pager.page = 1
    getLists()
    fetchDateCounts()
}

const getDictOptions = async () => {
    try {
        const [diagnosisType, syndromeType, assistants, deptTree] = await Promise.all([
            getDictData({ type: 'diagnosis_type' }),
            getDictData({ type: 'syndrome_type' }),
            getAssistants(),
            deptAll()
        ])
        diagnosisTypeOptions.value = diagnosisType?.diagnosis_type || []
        syndromeTypeOptions.value = syndromeType?.syndrome_type || []
        assistantOptions.value = assistants || []
        departmentFlatOptions.value = flattenDeptTree(Array.isArray(deptTree) ? deptTree : [])
    } catch (error) {
        console.error('获取字典数据失败:', error)
    }
}

// 获取字典标签
const getDictLabel = (options: any[], value: string) => {
    const item = options.find(opt => opt.value === value)
    return item ? item.name : value || '-'
}

const findAssistantOption = (assistantId: number | string | undefined | null) => {
    if (assistantId !== 0 && !assistantId) return null
    const id = typeof assistantId === 'string' ? parseInt(assistantId, 10) : assistantId
    return (
        assistantOptions.value.find((item) => {
            const itemId = typeof item.id === 'string' ? parseInt(item.id, 10) : item.id
            return itemId === id
        }) ?? null
    )
}

// 获取医助名称（列表未拉到医助字典时先显示「-」，避免与表格省略号混淆）
const getAssistantName = (assistantId: number | string) => {
    if (!assistantId && assistantId !== 0) return '-'
    if (assistantOptions.value.length === 0) return '-'
    const assistant = findAssistantOption(assistantId)
    return assistant ? assistant.name : '-'
}

/** 悬浮显示完整「姓名 / 部门」 */
const assistantCellTooltip = (row: any) => {
    const id = row.assistant_id || row.assistant
    const name = getAssistantName(id)
    const dept = getAssistantDeptNames(id)
    if (name === '-' && !dept) return ''
    return dept ? `${name} / ${dept}` : name
}

/** 医助所属部门（接口 getAssistants 返回 dept_names，多部门用顿号拼接） */
const getAssistantDeptNames = (assistantId: number | string) => {
    if (!assistantId && assistantId !== 0) return ''
    if (assistantOptions.value.length === 0) return ''
    const assistant = findAssistantOption(assistantId)
    const raw = assistant?.dept_names
    return typeof raw === 'string' ? raw.trim() : ''
}

const { pager, getLists, resetParams, resetPage } = usePaging({
    fetchFun: tcmDiagnosisLists,
    params: formData
})

/** 弹窗内保存后刷新列表：静默请求，避免 el-table v-loading 白蒙层盖住仍打开的抽屉 */
const refreshListAfterPopupSave = () => getLists({ silent: true })

// 重置
const handleReset = () => {
    activeTab.value = 'all'
    formData.patient_name = ''
    formData.diagnosis_type = ''
    formData.syndrome_type = ''
    formData.assistant_dept_id = ''
    formData.assistant_id = ''
    formData.diagnosis_confirmed = ''
    formData.appointment_date = ''
    formData.has_appointment = ''
    pager.page = 1
    getLists()
    fetchDateCounts()
}

// 是否已确认诊单
const isDiagnosisConfirmed = (row: any) => {
    const records = row.DiagnosisViewRecord || []
    return records.length > 0 && records.some((r: any) => r.is_confirmed == 1)
}

// 行高亮：未确认诊单优先，其次未挂号
const getRowClassName = ({ row }: { row: any }) => {
    if (!isDiagnosisConfirmed(row)) return 'row-unconfirmed'
    if (!row.has_appointment) return 'row-no-appointment'
    return ''
}

const editRef = ref()
const detailRef = ref()
const appointmentRef = ref()
/** 从路由带 id 进入时，诊单弹窗为异步组件，需在 ref 就绪后再 open */
const pendingRouteDiagnosisId = ref<number | null>(null)
const userStore = useUserStore()

watch(
    [editRef, pendingRouteDiagnosisId],
    () => {
        const id = pendingRouteDiagnosisId.value
        const opener = editRef.value as { open?: (mode: string, diagnosisId: number) => void } | undefined
        if (id == null || !opener?.open) return
        opener.open('edit', id)
        pendingRouteDiagnosisId.value = null
    },
    { flush: 'post' }
)

const watchCallVisible = ref(false)
const watchCallDiagnosisId = ref(0)

const isAssignedAssistant = (row: any) => {
    const aid = row.assistant_id ?? row.assistant
    if (aid === null || aid === undefined || aid === '') return false
    return Number(aid) === Number(userStore.userInfo?.id)
}

const openWatchCall = (row: any) => {
    watchCallDiagnosisId.value = Number(row.id) || 0
    watchCallVisible.value = true
}

type VideoCallHint = {
    state: string
    label: string
    start_time?: number
    end_time?: number
}

const canWatchCallEntry = (row: any) =>
    isAssignedAssistant(row) && hasPermission(['tcm.diagnosis/watchCall'])

const watchCallState = (row: any) => (row.video_call_hint?.state as string) || 'none'

const watchCallLiveTooltip = (row: any) => {
    const h = row.video_call_hint as VideoCallHint | undefined
    const base = '点击进入实时房间（仅观看，不推流、不上麦）'
    if (h?.start_time) {
        return `${base}。开始时间：${formatDateTime(h.start_time)}`
    }
    return base
}

/** 通话进行中或已发起待同步房间时，都显示「进入旁观」入口（避免仅 live 才有按钮导致看不见） */
const watchCallShowEnterButton = (row: any) => {
    const st = watchCallState(row)
    return st === 'live' || st === 'pending_room'
}

const watchCallEnterTooltip = (row: any) => {
    if (watchCallState(row) === 'pending_room') {
        return '医生尚未接通或未同步房间号，接通后再点此进入'
    }
    return watchCallLiveTooltip(row)
}

const onWatchCallEntryClick = (row: any) => {
    if (watchCallState(row) !== 'live') {
        feedback.msgWarning('医生尚未接通或未同步房间号，请稍后再试')
        return
    }
    openWatchCall(row)
}

const watchCallAssistantIdleText = (row: any) => {
    const h = row.video_call_hint as VideoCallHint | undefined
    if (h?.label) return h.label
    return '暂无可旁观通话'
}

const watchCallPublicStatus = (row: any) => {
    const st = watchCallState(row)
    if (st === 'none') return '—'
    if (st === 'live') return '通话中'
    if (st === 'pending_room') return '接通中'
    const h = row.video_call_hint as VideoCallHint | undefined
    return h?.label || '—'
}

// 创建订单相关
const createOrderVisible = ref(false)
const orderFormRef = ref()
const orderLoading = ref(false)
const orderForm = reactive({
    patient_id: '' as string | number,
    patient_name: '',
    phone: '',
    order_type: '' as string | number,
    amount: 0,
    remark: '',
    has_appointment: false,
    appointment_time_text: ''
})
const orderRules = {
    order_type: [{ required: true, message: '请选择订单类型', trigger: 'change' }],
    amount: [{ required: true, message: '请输入订单金额', trigger: 'blur' }]
}

const handleCreateOrder = (row: any) => {
    orderForm.patient_id = row.patient_id || row.id
    orderForm.patient_name = row.patient_name
    orderForm.phone = row.phone || ''
    orderForm.order_type = ''
    orderForm.amount = 0
    orderForm.remark = ''
    orderForm.has_appointment = !!row.has_appointment
    orderForm.appointment_time_text = row.appointment_time_text || ''
    orderFormRef.value?.clearValidate()
    createOrderVisible.value = true
}

const resetOrderForm = () => {
    orderForm.patient_id = ''
    orderForm.patient_name = ''
    orderForm.phone = ''
    orderForm.order_type = ''
    orderForm.amount = 0
    orderForm.remark = ''
    orderForm.has_appointment = false
    orderForm.appointment_time_text = ''
    orderFormRef.value?.clearValidate()
}

const submitOrder = async () => {
    try {
        await orderFormRef.value?.validate()
        orderLoading.value = true
        const res = await orderCreate({
            patient_id: orderForm.patient_id,
            order_type: orderForm.order_type,
            amount: orderForm.amount,
            remark: orderForm.remark
        })
        createOrderVisible.value = false
        getLists()

        const orderNo = res?.order_no
        if (orderNo) {
            qrcodeDialogVisible.value = true
            qrcodeLoading.value = true
            qrcodeUrl.value = ''
            currentQRCodePatient.value = {
                patient_name: orderForm.patient_name,
                has_appointment: orderForm.has_appointment,
                appointment_time_text: orderForm.appointment_time_text
            }
            try {
                const qrRes = await generateOrderQrcode({ order_no: orderNo })
                if (qrRes?.qrcode_url) {
                    qrcodeUrl.value = qrRes.qrcode_url
                } else {
                    feedback.msgError('二维码生成失败')
                }
            } catch (e: any) {
                feedback.msgError(e?.msg || '生成二维码失败')
            } finally {
                qrcodeLoading.value = false
            }
        }
    } catch (error) {
        console.error('创建订单失败:', error)
    } finally {
        orderLoading.value = false
    }
}

// 小程序二维码相关
const qrcodeDialogVisible = ref(false)
const qrcodeLoading = ref(false)
const qrcodeUrl = ref('')
const currentQRCodePatient = ref<any>(null)
/** 二维码弹窗展示的挂号时间（与列表「挂号」列一致） */
const qrcodeAppointmentTimeText = computed(() => {
    const p = currentQRCodePatient.value
    if (!p) return '—'
    if (!p.has_appointment) return '未挂号'
    return p.appointment_time_text || '—'
})
const qrcodeDialogTitle = ref('小程序二维码')
const lastQRCodeType = ref<'video' | 'confirm'>('confirm')

// 批量指派相关
const selectedIds = ref<number[]>([])
const assignDialogVisible = ref(false)
const assignLoading = ref(false)
const assignMode = ref<'batch' | 'single'>('batch')
const currentRow = ref<any>(null)
const assignForm = ref({
    assistant_id: null as number | null,
    is_inherit: false
})

const handleSelectionChange = (selection: any[]) => {
    selectedIds.value = selection.map(item => item.id)
}

// 单独指派
const handleSingleAssign = (row: any) => {
    assignMode.value = 'single'
    currentRow.value = row
    // 确保类型一致，转换为数字
    assignForm.value.assistant_id = Number(row.assistant_id) > 0 ? Number(row.assistant_id) : null
    assignForm.value.is_inherit = false
    assignDialogVisible.value = true
}

// 批量指派
const handleBatchAssign = () => {
    if (selectedIds.value.length === 0) {
        feedback.msgWarning('请先选择要指派的诊单')
        return
    }
    assignMode.value = 'batch'
    currentRow.value = null
    assignForm.value.assistant_id = null
    assignForm.value.is_inherit = false
    assignDialogVisible.value = true
}

const handleConfirmAssign = async () => {
    if (!assignForm.value.assistant_id) {
        feedback.msgWarning('请选择医助')
        return
    }
    
    assignLoading.value = true
    try {
        if (assignMode.value === 'single') {
            // 单独指派
            await tcmDiagnosisAssign({ 
                id: currentRow.value.id, 
                assistant_id: assignForm.value.assistant_id,
                is_inherit: assignForm.value.is_inherit ? 1 : 0
            })
        } else {
            // 批量指派
            const promises = selectedIds.value.map(id => 
                tcmDiagnosisAssign({ 
                    id, 
                    assistant_id: assignForm.value.assistant_id,
                    is_inherit: assignForm.value.is_inherit ? 1 : 0
                })
            )
            await Promise.all(promises)
            selectedIds.value = []
        }
        
        assignDialogVisible.value = false
        getLists()
    } catch (error) {
        console.error('指派失败:', error)
    } finally {
        assignLoading.value = false
    }
}

const handleAdd = () => {
    editRef.value.open('add')
}

const handleEdit = (id: number) => {
    editRef.value.open('edit', id)
}

const handleDetail = (id: number) => {
    detailRef.value.open(id)
}

const handleDelete = async (id: number) => {
    await feedback.confirm('确定要删除该诊单吗？')
    await tcmDiagnosisDelete({ id })
    getLists()
}

// 行操作下拉
const handleRowAction = (cmd: string, row: any) => {
    switch (cmd) {
        case 'assign': handleSingleAssign(row); break
        case 'videoQr':
            if (!isAppointmentActiveForVideo(row)) {
                feedback.msgWarning('仅「已预约」状态可生成视频二维码')
                return
            }
            handleVideoQRCode(row)
            break
        case 'confirmQr': handleMiniProgramQRCode(row); break
        case 'cancelApt': handleCancelAppointment(row); break
        case 'order': handleCreateOrder(row); break
        case 'delete': handleDelete(row.id); break
    }
}

/** 挂号状态：1=已预约 3=已完成 4=已过号（与后端一致） */
const appointmentStatusLabel = (row: any) => {
    const s = Number(row.appointment_status)
    if (s === 3) return '已完成'
    if (s === 4) return '已过号'
    return '已挂号'
}

const appointmentCellClasses = (row: any) => {
    const s = Number(row.appointment_status)
    return {
        'has-apt': row.has_appointment,
        'apt-row-done': row.has_appointment && s === 3,
        'apt-row-missed': row.has_appointment && s === 4
    }
}

/** 仅已预约(1)可进视频/小程序码 */
const isAppointmentActiveForVideo = (row: any) =>
    row.has_appointment && Number(row.appointment_status) === 1

/** 已预约、已过号可取消（后端同步限制） */
const canCancelAppointmentRow = (row: any) => {
    const s = Number(row.appointment_status)
    return !!(row.has_appointment && row.appointment_id && (s === 1 || s === 4))
}

// 取消挂号
const handleCancelAppointment = async (row: any) => {
    if (!canCancelAppointmentRow(row)) {
        const s = Number(row.appointment_status)
        if (row.has_appointment && s === 3) {
            feedback.msgWarning('已完成就诊，无法取消挂号')
        } else {
            feedback.msgWarning('该诊单未挂号或挂号信息异常')
        }
        return
    }
    try {
        await feedback.confirm(`确定要取消患者「${row.patient_name}」的挂号吗？`)
        await cancelAppointment({ id: row.appointment_id })
        feedback.msgSuccess('取消挂号成功')
        getLists()
        fetchDateCounts()
    } catch (e: any) {
        if (e !== 'cancel') feedback.msgError(e?.msg || '取消挂号失败')
    }
}

// 患者挂号
const handleAppointment = (row: any) => {
    if (!row.patient_id) {
        feedback.msgWarning('患者信息不完整')
        return
    }
    
    appointmentRef.value?.open(row)
}

// 补全身份证
const fillIdCardVisible = ref(false)
const fillIdCardLoading = ref(false)
const fillIdCardFormRef = ref()
const fillIdCardForm = reactive({
    id: 0,
    patient_name: '',
    id_card: ''
})
const validateIdCard = (_rule: any, value: string, callback: (e?: Error) => void) => {
    if (!value) {
        callback(new Error('请输入身份证号'))
        return
    }
    const valid18 = /^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$/.test(value)
    const valid15 = /^[1-9]\d{5}\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}$/.test(value)
    if (valid18 || valid15) {
        callback()
    } else {
        callback(new Error('请输入15或18位有效身份证号'))
    }
}
const fillIdCardRules = {
    id_card: [{ required: true, validator: validateIdCard, trigger: 'blur' }]
}
const handleFillIdCard = (row: any) => {
    fillIdCardForm.id = row.id
    fillIdCardForm.patient_name = row.patient_name || '-'
    fillIdCardForm.id_card = row.id_card || ''
    fillIdCardVisible.value = true
}
const submitFillIdCard = async () => {
    await fillIdCardFormRef.value?.validate()
    fillIdCardLoading.value = true
    try {
        await fillIdCard({ id: fillIdCardForm.id, id_card: fillIdCardForm.id_card })
        feedback.msgSuccess('补全成功，年龄已自动更新')
        fillIdCardVisible.value = false
        getLists()
    } catch (e: any) {
        feedback.msgError(e?.msg || '补全失败')
    } finally {
        fillIdCardLoading.value = false
    }
}

// 生成视频二维码（跳转登录页）- 仅已预约(1)可生成
const handleVideoQRCode = async (row: any) => {
    if (!isAppointmentActiveForVideo(row)) {
        feedback.msgWarning('仅「已预约」状态可生成视频二维码')
        return
    }
    if (!row.patient_id) {
        feedback.msgWarning('患者信息不完整')
        return
    }
    lastQRCodeType.value = 'video'
    currentQRCodePatient.value = row
    qrcodeDialogVisible.value = true
    qrcodeLoading.value = true
    qrcodeUrl.value = ''
    qrcodeDialogTitle.value = '视频二维码'
    try {
        const config = await getWeappConfig()
        if (!config?.app_id) {
            feedback.msgError('小程序未配置，请先配置小程序信息')
            qrcodeDialogVisible.value = false
            return
        }
        const result = await generateMiniProgramQrcode({
            doctor_id: row.appointment_doctor_id,
            diagnosis_id: row.appointment_doctor_id,
            patient_id: row.patient_id,
            share_user_id: userStore.userInfo?.id || '',
            mini_program_path: 'pages/login/login'
        })
        if (result?.qrcode_url) {
            qrcodeUrl.value = result.qrcode_url
        } else {
            feedback.msgError('二维码生成失败')
        }
    } catch (error: any) {
        feedback.msgError(error?.msg || '生成二维码失败，请重试')
    } finally {
        qrcodeLoading.value = false
    }
}

// 生成确认诊单二维码
const handleMiniProgramQRCode = async (row: any) => {
    if (!isAppointmentActiveForVideo(row)) {
        feedback.msgWarning('仅「已预约」状态可使用诊单二维码')
        return
    }
    if (!row.patient_id) {
        feedback.msgWarning('患者信息不完整')
        return
    }
    lastQRCodeType.value = 'confirm'
    currentQRCodePatient.value = row
    qrcodeDialogVisible.value = true
    qrcodeLoading.value = true
    qrcodeUrl.value = ''
    qrcodeDialogTitle.value = '诊单二维码'
    
    try {
        // 获取小程序配置
        const config = await getWeappConfig()
        
        if (!config?.app_id) {
            feedback.msgError('小程序未配置，请先配置小程序信息')
            qrcodeDialogVisible.value = false
            return
        }
        
        // 获取当前登录用户信息
        const currentUser = userStore.userInfo
        
        // 调用生成二维码接口
        const result = await generateMiniProgramQrcode({
            diagnosis_id: row.id,
             doctor_id: row.appointment_doctor_id,
            patient_id: row.patient_id,
            share_user_id: currentUser?.id || ''
        })
        
        if (result?.qrcode_url) {
            qrcodeUrl.value = result.qrcode_url
        } else {
            feedback.msgError('二维码生成失败')
        }
    } catch (error: any) {
        console.error('生成小程序二维码失败:', error)
        feedback.msgError(error?.msg || '生成二维码失败，请重试')
    } finally {
        qrcodeLoading.value = false
    }
}

// 重新生成二维码
const handleRegenerateQRCode = () => {
    if (currentQRCodePatient.value) {
        lastQRCodeType.value === 'video'
            ? handleVideoQRCode(currentQRCodePatient.value)
            : handleMiniProgramQRCode(currentQRCodePatient.value)
    }
}

// ========== 企业微信聊天记录 ==========
const wechatDrawerVisible = ref(false)
const wechatCurrentPatient = ref<any>(null)
const wechatRecords = ref<any[]>([])
const wechatRecordTotal = ref(0)
const wechatRecordsLoading = ref(false)
const wechatContactInfo = ref<any>(null)
const wechatNewNote = ref('')
const wechatAddLoading = ref(false)
const wechatPage = ref(1)

const formatTimestamp = (ts: number) => {
    if (!ts) return '-'
    const t = String(ts).length === 10 ? ts * 1000 : ts
    return dayjs(t).format('YYYY-MM-DD HH:mm')
}

const handleWechatRecords = async (row: any) => {
    wechatCurrentPatient.value = row
    wechatDrawerVisible.value = true
    wechatRecords.value = []
    wechatRecordTotal.value = 0
    wechatContactInfo.value = null
    wechatNewNote.value = ''
    wechatPage.value = 1

    // 并行加载聊天记录和外部联系人信息
    wechatRecordsLoading.value = true
    try {
        const [recordsRes, contactRes] = await Promise.all([
            getWechatChatRecords({ diagnosis_id: row.id, patient_id: row.patient_id, page_no: 1, page_size: 20 }),
            getWechatExternalContact({ patient_id: row.patient_id || row.id }).catch(() => null)
        ])

        wechatRecords.value = recordsRes?.lists || []
        wechatRecordTotal.value = recordsRes?.count || 0
        wechatContactInfo.value = contactRes
    } catch (e) {
        console.error('加载企业微信记录失败:', e)
    } finally {
        wechatRecordsLoading.value = false
    }
}

const loadMoreRecords = async () => {
    if (!wechatCurrentPatient.value) return
    wechatPage.value++
    try {
        const res = await getWechatChatRecords({
            diagnosis_id: wechatCurrentPatient.value.id,
            patient_id: wechatCurrentPatient.value.patient_id,
            page_no: wechatPage.value,
            page_size: 20
        })
        wechatRecords.value.push(...(res?.lists || []))
    } catch (e) {
        console.error('加载更多记录失败:', e)
    }
}

const handleAddRecord = async () => {
    if (!wechatNewNote.value.trim()) {
        feedback.msgWarning('请输入内容')
        return
    }
    wechatAddLoading.value = true
    try {
        await addWechatChatRecord({
            diagnosis_id: wechatCurrentPatient.value.id,
            patient_id: wechatCurrentPatient.value.patient_id,
            content: wechatNewNote.value.trim(),
            msg_type: 'note',
            staff_name: userStore.userInfo?.name || '',
            external_name: wechatCurrentPatient.value.patient_name || '',
        })
        wechatNewNote.value = ''
        // 重新加载记录
        handleWechatRecords(wechatCurrentPatient.value)
    } catch (e: any) {
        feedback.msgError(e?.msg || '添加失败')
    } finally {
        wechatAddLoading.value = false
    }
}

const handleDeleteRecord = async (id: number) => {
    await feedback.confirm('确定删除该记录吗？')
    try {
        await deleteWechatChatRecord({ id })
        wechatRecords.value = wechatRecords.value.filter(r => r.id !== id)
        wechatRecordTotal.value--
    } catch (e: any) {
        feedback.msgError(e?.msg || '删除失败')
    }
}

const route = useRoute()
onMounted(() => {
    // 默认展示当天挂号，突出重点
    formData.appointment_date = todayStr.value
    // 字典与列表并行，避免 await 字典阻塞首屏列表（LCP）
    getDictOptions()
    getLists()
    scheduleDeferredDateCounts()

    silentListPollTimer = setInterval(silentRefreshListsAndDateCounts, SILENT_LIST_POLL_MS)

    const raw = route.query.id
    if (raw != null && raw !== '') {
        const num = Number(raw)
        if (!Number.isNaN(num)) pendingRouteDiagnosisId.value = num
    }
})

onUnmounted(() => {
    if (silentListPollTimer != null) {
        clearInterval(silentListPollTimer)
        silentListPollTimer = null
    }
})
</script>

<style lang="scss" scoped>
.tcm-diagnosis {
  
    min-height: 100%;
    background: var(--el-bg-color-page);
 
    margin: 0 auto;
}

.filter-card {
    :deep(.el-card__body) {
        padding: 12px 16px;
    }
}

.filter-main {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    margin-bottom: 8px;
}

.date-chips {
    display: flex;
    gap: 6px;

    .chip {
        padding: 6px 12px;
        font-size: 14px;
        font-weight: 500;
        color: var(--el-text-color-regular);
        background: var(--el-fill-color-light);
        border-radius: 6px;
        cursor: pointer;
        transition: all 0.2s;

        em {
            font-style: normal;
            margin-left: 4px;
            opacity: 0.8;
        }

        &:hover {
            background: var(--el-fill-color);
        }

        &.active {
            color: #fff;
            background: var(--el-color-primary);
        }
    }
}

.search-row {
    display: flex;
    gap: 8px;

    .search-input {
        width: 160px;
    }
}

.filter-chips {
    display: flex;
    align-items: center;
    gap: 6px;
    padding-top: 8px;
    margin-top: 8px;
    border-top: 1px solid var(--el-border-color-lighter);

    .chip-label {
        font-size: 13px;
        color: var(--el-text-color-secondary);
        margin-right: 2px;
    }

    .chip {
        padding: 3px 10px;
        font-size: 13px;
        color: var(--el-text-color-secondary);
        background: transparent;
        border-radius: 4px;
        cursor: pointer;
        transition: all 0.2s;

        &:hover {
            color: var(--el-color-primary);
            background: var(--el-color-primary-light-9);
        }

        &.active {
            color: var(--el-color-primary);
            font-weight: 500;
            background: var(--el-color-primary-light-9);
        }
    }

    .chip-sm {
        padding: 4px 10px;
    }

    .chip-divider {
        width: 1px;
        height: 14px;
        background: var(--el-border-color-lighter);
        margin: 0 4px;
    }

    .more-btn {
        margin-left: 8px;

        :deep(.el-icon.rotate-180) {
            transform: rotate(180deg);
        }
    }
}

.filter-more {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px;
    margin-top: 8px;
    padding-top: 8px;
    border-top: 1px dashed var(--el-border-color-lighter);

    .filter-dept-select {
        min-width: 168px;
    }
}

.list-card {
    :deep(.el-card__body) {
        padding: 0;
    }
}

.list-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 10px 16px;
    border-bottom: 1px solid var(--el-border-color-lighter);

    .list-actions {
        display: flex;
        gap: 8px;
    }

    .list-selected {
        font-size: 13px;
        color: var(--el-text-color-secondary);
    }
}

.table-wrap {
    padding: 10px 16px 0;
}

.pagination-wrap {
    display: flex;
    justify-content: flex-end;
    padding: 10px 16px 16px;
}

.patient-cell {
    display: flex;
    align-items: center;
    gap: 8px;

    .patient-avatar {
        width: 32px;
        height: 32px;
        border-radius: 8px;
        background: linear-gradient(135deg, var(--el-color-primary-light-5), var(--el-color-primary-light-7));
        color: var(--el-color-primary);
        display: flex;
        align-items: center;
        justify-content: center;
        font-size: 14px;
        font-weight: 600;
        flex-shrink: 0;
    }

    .patient-info {
        min-width: 0;
    }

    .patient-name {
        font-weight: 600;
        font-size: 14px;
        color: var(--el-text-color-primary);
        line-height: 1.3;
    }

    .patient-meta {
        font-size: 12px;
        color: var(--el-text-color-placeholder);
        margin-top: 2px;
    }
}

.patient-meta-col {
    font-size: 12px;
    color: var(--el-text-color-placeholder);
    line-height: 1.4;
}

.appointment-cell {
    padding: 4px 8px;
    border-radius: 6px;
    min-height: 36px;

    &.has-apt {
        background: linear-gradient(135deg, rgba(74, 93, 255, 0.12), rgba(74, 93, 255, 0.06));
        border: 1px solid rgba(74, 93, 255, 0.3);

        .apt-badge {
            display: inline-block;
            font-size: 11px;
            font-weight: 600;
            color: #3a4acc;
            background: rgba(74, 93, 255, 0.15);
            padding: 2px 6px;
            border-radius: 4px;
            margin-bottom: 2px;
        }

        .apt-doctor {
            font-size: 14px;
            font-weight: 600;
            color: var(--el-text-color-primary);
        }

        .apt-time {
            font-size: 12px;
            color: #4A5DFF;
            margin-top: 2px;
        }
    }

    &.apt-row-missed {
        background: linear-gradient(135deg, rgba(var(--el-color-warning-rgb), 0.12), rgba(var(--el-color-warning-rgb), 0.05));
        border-color: rgba(var(--el-color-warning-rgb), 0.35);

        .apt-badge {
            color: var(--el-color-warning-dark-2);
            background: var(--el-color-warning-light-7);
        }

        .apt-time {
            color: var(--el-color-warning);
        }
    }

    &.apt-row-done {
        background: linear-gradient(135deg, rgba(var(--el-color-info-rgb), 0.1), rgba(var(--el-color-info-rgb), 0.04));
        border-color: rgba(var(--el-color-info-rgb), 0.28);

        .apt-badge {
            color: var(--el-color-info-dark-2);
            background: var(--el-color-info-light-7);
        }

        .apt-time {
            color: var(--el-color-info);
        }
    }

    .apt-none {
        font-size: 13px;
        color: var(--el-text-color-placeholder);
    }
}

.status-confirmed {
    font-size: 13px;
    color: var(--el-color-success);
    font-weight: 500;
}

.status-unconfirmed {
    font-size: 13px;
    color: var(--el-color-warning);
    font-weight: 600;
}

.status-prescribed {
    font-size: 13px;
    color: #4A5DFF;
    font-weight: 500;
}

.status-unprescribed {
    font-size: 13px;
    color: var(--el-text-color-placeholder);
}

.diagnosis-cell {
    display: flex;
    flex-direction: column;
    gap: 2px;

    .diagnosis-date {
        font-size: 14px;
        font-weight: 500;
        color: var(--el-text-color-primary);
    }

    .diagnosis-extra {
        font-size: 12px;
        color: var(--el-text-color-secondary);
    }
}

.followup-cell {
    display: flex;
    flex-direction: column;
    gap: 4px;
    line-height: 1.35;

    .followup-time {
        font-size: 13px;
        font-weight: 500;
        color: var(--el-text-color-primary);
    }

    .followup-doctor {
        font-size: 12px;
        color: var(--el-text-color-secondary);
    }

    .followup-count {
        font-size: 12px;
        color: var(--el-text-color-regular);
    }

    .followup-void-tag {
        align-self: flex-start;
    }
}

.followup-none {
    font-size: 13px;
    color: var(--el-text-color-placeholder);
}

.assistant-cell {
    display: flex;
    flex-direction: column;
    gap: 2px;
    line-height: 1.35;
    min-width: 0;
    white-space: normal;
    overflow-wrap: break-word;

    .assistant-name {
        font-size: 13px;
        color: var(--el-text-color-primary);
    }

    .assistant-dept {
        font-size: 12px;
        color: var(--el-text-color-secondary);
    }
}

.prescription-status-cell {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: 0 2px;
    line-height: 1.3;

    .prescription-times {
        font-size: 12px;
        color: var(--el-text-color-secondary);
    }
}

.action-cell {
    display: flex;
    align-items: center;
    gap: 4px;
    flex-wrap: wrap;
}

.video-watch-col {
    font-size: 12px;
    line-height: 1.4;
    padding: 0 2px;
}

/* el-tooltip 单个子节点为行内按钮时，包一层避免触发区域为 0 或错位 */
.video-watch-trigger {
    display: inline-block;
    vertical-align: middle;
}

.video-watch-muted {
    color: var(--el-text-color-secondary);
}

.text-danger {
    color: var(--el-color-danger);
}

.diagnosis-table {
    :deep(.el-table__header th) {
        background: var(--el-fill-color-light);
        font-weight: 600;
        font-size: 13px;
        color: var(--el-text-color-regular);
    }
    :deep(.el-table__row) {
        cursor: default;
    }
    :deep(.el-table__body .el-table__cell) {
        padding: 8px 0;
    }
    :deep(.el-table__row.row-unconfirmed) {
        border-left: 3px solid var(--el-color-warning);
        background: linear-gradient(90deg, rgba(var(--el-color-warning-rgb), 0.06) 0%, transparent 100%) !important;
    }
    :deep(.el-table__row.row-unconfirmed:hover > td) {
        background: linear-gradient(90deg, rgba(var(--el-color-warning-rgb), 0.1) 0%, transparent 100%) !important;
    }
    :deep(.el-table__row.row-no-appointment) {
        border-left: 3px solid var(--el-color-info);
        background: linear-gradient(90deg, rgba(var(--el-color-info-rgb), 0.04) 0%, transparent 100%) !important;
    }
    :deep(.el-table__row.row-no-appointment:hover > td) {
        background: linear-gradient(90deg, rgba(var(--el-color-info-rgb), 0.08) 0%, transparent 100%) !important;
    }
}

.record-list {
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.record-item {
    background: #f9fafb;
    border-radius: 8px;
    padding: 12px;
    border: 1px solid #f0f0f0;
}

.record-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 8px;
}

.record-content {
    font-size: 14px;
    line-height: 1.6;
    color: #333;
    white-space: pre-wrap;
    word-break: break-all;
}
</style>
