<template>
  <div class="exam-page">
    <!-- 功能标签�?-->
    <div class="main-tabs-bar">
      <button 
        v-if="canShowGenerate"
        :class="['main-tab-btn', { active: activeTab === 'generate' }]" 
        @click="activeTab = 'generate'"
      >
        <span class="tab-icon">🤖</span>
        <span class="tab-text">AI出题</span>
      </button>
      <button 
        v-if="canShowBank"
        :class="['main-tab-btn', { active: activeTab === 'bank' }]" 
        @click="activeTab = 'bank'"
      >
        <span class="tab-icon">📚</span>
        <span class="tab-text">题库管理</span>
      </button>
      <button 
        v-if="canShowPaper"
        :class="['main-tab-btn', { active: activeTab === 'paper' }]" 
        @click="activeTab = 'paper'"
      >
        <span class="tab-icon">📋</span>
        <span class="tab-text">智能组卷</span>
      </button>
      <button 
        v-if="canShowTrain"
        :class="['main-tab-btn', { active: activeTab === 'train' }]" 
        @click="activeTab = 'train'"
      >
        <span class="tab-icon">🎯</span>
        <span class="tab-text">互动训练</span>
      </button>
      <button
        v-if="canShowExam"
        :class="['main-tab-btn', { active: activeTab === 'exam' }]"
        @click="activeTab = 'exam'"
      >
        <span class="tab-icon">📝</span>
        <span class="tab-text">试卷考核</span>
      </button>
    </div>

    <!-- ========== AI出题面板 ========== -->
    <div v-show="activeTab === 'generate'" class="exam-container">
      <!-- 顶部标题�?-->
      <div class="exam-header">
        <h1 class="exam-title">📚 AI智能出题系统</h1>
        <div class="user-info">
          <span>👤 当前用户</span>
        </div>
      </div>

      <!-- 统计卡片 -->
      <div class="stats-row">
        <div class="stat-card">
          <div class="stat-icon">📝</div>
          <div class="stat-info">
            <div class="stat-value">{{ typeStats.single }}</div>
            <div class="stat-label">单选题</div>
          </div>
        </div>
        <div class="stat-card">
          <div class="stat-icon">📋</div>
          <div class="stat-info">
            <div class="stat-value">{{ typeStats.multiple }}</div>
            <div class="stat-label">多选题</div>
          </div>
        </div>
        <div class="stat-card">
          <div class="stat-icon">�?/div>
          <div class="stat-info">
            <div class="stat-value">{{ typeStats.judgment }}</div>
            <div class="stat-label">判断�?/div>
          </div>
        </div>
        <div class="stat-card">
          <div class="stat-icon">📄</div>
          <div class="stat-info">
            <div class="stat-value">{{ typeStats.essay }}</div>
            <div class="stat-label">简答题</div>
          </div>
        </div>
        <div class="stat-card stat-card-total">
          <div class="stat-icon">📊</div>
          <div class="stat-info">
            <div class="stat-value">{{ typeStats.total }}</div>
            <div class="stat-label">合计</div>
          </div>
        </div>
      </div>

      <!-- 出题表单卡片 -->
      <div class="exam-card">
        <div class="card-header">
          <h2>🤖 智能出题配置</h2>
        </div>

        <div class="form-group">
          <label class="form-label">选择制度文件 <span class="required-mark">*</span></label>
          <FileSelector 
            v-model="selectedDoc" 
            @change="onDocSelected" 
            @collection-change="onCollectionChange"
            placeholder="先选择知识库，再选择制度文件..." 
          />
          <div v-if="selectedCollectionName" class="collection-info">
            <span class="collection-icon">📚</span>
            <span>当前知识�? <strong>{{ selectedCollectionName }}</strong></span>
          </div>
          <div v-if="selectedDocData" class="selected-file-info">
            <span class="file-info-icon">📄</span>
            <span class="file-info-name">{{ selectedDocData.title || selectedDocData.rawFileName }}</span>
            <span class="file-info-version">v{{ selectedDocData.version }}</span>
          </div>
        </div>

        <div class="form-group">
          <label class="form-label">难度等级</label>
          <select v-model="difficulty" class="form-select">
            <option :value="1">简�?/option>
            <option :value="2">中等</option>
            <option :value="3">困难</option>
          </select>
        </div>

        <div class="form-group">
          <label class="form-label">题型与数量配�?/label>
          <div class="type-count-grid">
            <div class="type-count-item" v-for="qt in questionTypeOptions" :key="qt.value">
              <label class="checkbox-item type-count-label" :class="{ active: questionTypeCounts[qt.value] > 0 }">
                <input type="checkbox" :checked="questionTypeCounts[qt.value] > 0" @change="toggleQuestionType(qt.value)" />
                {{ qt.label }}
              </label>
              <input 
                type="number" 
                v-model.number="questionTypeCounts[qt.value]" 
                min="0" 
                max="50" 
                class="form-input type-count-input" 
                :disabled="questionTypeCounts[qt.value] <= 0"
                placeholder="0"
              />
            </div>
          </div>
          <div class="type-count-total">
            总计: <strong>{{ totalQuestionCount }}</strong> �?          </div>
        </div>

        <div class="form-actions">
          <button class="btn btn-primary" @click="handleGenerateQuestions">
            🚀 开始生成题�?          </button>
          <button class="btn btn-secondary" @click="generatedQuestions = []">
            🗑�?清空结果
          </button>
        </div>
      </div>

      <!-- 待审核题目列表（常驻展示�?-->
      <div class="exam-card">
        <div class="card-header">
          <h2>📋 待审核题目列�?            <span v-if="questionPagination.total > 0">
              （共 {{ questionPagination.total }} 题，�?{{ questionPagination.current }}/{{ totalPages }} 页）
            </span>
          </h2>
          <div class="header-actions">
            <select v-model="pendingListStatus" @change="loadGeneratedQuestions(1)" class="form-select filter-select-sm">
              <option value="all">全部状�?/option>
              <option value="approved">已审�?/option>
              <option value="pending">待审�?/option>
              <option value="rejected">禁用</option>
            </select>
            <button
              class="btn btn-primary btn-sm"
              @click="refreshQuestionList"
              :disabled="questionPagination.loading"
            >
              {{ questionPagination.loading ? '�?刷新�?..' : '🔄 刷新列表' }}
            </button>
            <button
              v-if="questionPagination.total > 0"
              class="btn btn-success btn-sm"
              @click="message.success('批量审批功能开发中...')"
            >
              �?批量通过
            </button>
          </div>
        </div>

        <!-- 加载状�?-->
        <div v-if="questionPagination.loading && generatedQuestions.length === 0" class="loading-state">
          <div class="loading-spinner"></div>
          <p>正在加载待审核题�?..</p>
        </div>

        <!-- 空状�?-->
        <div v-else-if="!questionPagination.loading && generatedQuestions.length === 0 && questionPagination.total === 0" class="empty-state">
          <div class="empty-icon">📭</div>
          <h3>暂无待审核题�?/h3>
          <p>当前没有需要审批的题目</p>
          <div class="empty-tips">
            <p>💡 提示�?/p>
            <ul>
              <li>使用上方"AI出题"功能生成新题�?/li>
              <li>生成的题目会自动出现在此列表�?/li>
              <li>您可以随时刷新查看最新待审核题目</li>
            </ul>
          </div>
        </div>

        <!-- 题目列表 -->
        <div v-else class="questions-list">
          <div v-for="(q, index) in generatedQuestions" :key="q.id || index" class="question-item">
            <div class="question-header">
              <span :class="['type-badge', q.typeClass]">
                {{ q.typeLabel }}
              </span>
              <span class="question-number">�?{{ (questionPagination.current - 1) * questionPagination.pageSize + index + 1 }} �?/span>
              <div class="question-actions">
                <template v-if="q.status === 'pending'">
                  <button class="btn btn-success btn-xs" @click="handleApproveQuestion(q)">�?采纳</button>
                  <button class="btn btn-danger btn-xs" @click="handleRejectQuestion(q)">�?拒绝</button>
                </template>
                <span v-else-if="q.status === 'approved'" class="status-tag status-approved">�?已审�?/span>
                <span v-else-if="q.status === 'rejected'" class="status-tag status-rejected">�?已拒�?/span>
                <button class="btn btn-warning btn-xs" @click="handleEditQuestion(q)">✏️ 编辑</button>
              </div>
            </div>

            <div class="question-content">
              <strong>{{ q.stem }}</strong>
            </div>

            <!-- 选择�?多选题：显示options并高亮正确答�?-->
            <div v-if="(q.type === 'single_choice' || q.type === 'multiple_choice') && q.options && q.options.length > 0" class="question-options">
              <div v-for="(opt, idx) in q.options" :key="idx"
                class="question-option"
                :class="{ 'option-correct': isOptionCorrect(q, opt.key) }">
                <span class="option-letter">{{ opt.key }}.</span>
                <span class="option-text">{{ opt.content }}</span>
                <span v-if="isOptionCorrect(q, opt.key)" class="correct-badge">�?正确</span>
              </div>
            </div>

            <!-- 判断题：只显示两个选项，高亮正确答�?-->
            <div v-if="q.type === 'true_false'" class="question-options">
              <div class="question-option" :class="{ 'option-correct': q.answer === 'T' || q.answer === 'true' }">
                <span class="option-letter">A.</span><span class="option-text">正确</span>
                <span v-if="q.answer === 'T' || q.answer === 'true'" class="correct-badge">�?正确答案</span>
              </div>
              <div class="question-option" :class="{ 'option-correct': q.answer === 'F' || q.answer === 'false' }">
                <span class="option-letter">B.</span><span class="option-text">错误</span>
                <span v-if="q.answer === 'F' || q.answer === 'false'" class="correct-badge">�?正确答案</span>
              </div>
            </div>

            <div class="question-answer">
              <strong>�?参考答案：</strong>{{ Array.isArray(q.answer) ? q.answer.join(', ') : q.answer }}
            </div>

            <div v-if="q.explanation" class="question-analysis">
              <strong>💡 解析�?/strong>{{ q.explanation }}
            </div>
          </div>
        </div>

        <!-- 分页控件 -->
        <div v-if="questionPagination.total > questionPagination.pageSize" class="pagination-wrapper">
          <div class="pagination-info">
            �?<strong>{{ questionPagination.total }}</strong> 道题目，
            �?<strong>{{ (questionPagination.current - 1) * questionPagination.pageSize + 1 }}-{{ Math.min(questionPagination.current * questionPagination.pageSize, questionPagination.total) }}</strong> �?          </div>

          <div class="pagination-controls">
            <button
              class="btn btn-pagination"
              :disabled="questionPagination.current <= 1 || questionPagination.loading"
              @click="loadGeneratedQuestions(1)"
            >
              首页
            </button>
            <button
              class="btn btn-pagination"
              :disabled="questionPagination.current <= 1 || questionPagination.loading"
              @click="loadGeneratedQuestions(questionPagination.current - 1)"
            >
              ⬅️ 上一�?            </button>

            <div class="pagination-pages">
              <button
                v-for="page in getVisiblePages(questionPagination.current, totalPages)"
                :key="'p-' + page"
                :class="['btn', 'btn-page', { active: page === questionPagination.current }]"
                :disabled="page === questionPagination.current || page === '...' || questionPagination.loading"
                @click="page !== questionPagination.current && page !== '...' && loadGeneratedQuestions(page)"
              >
                {{ page }}
              </button>
            </div>

            <button
              class="btn btn-pagination"
              :disabled="questionPagination.current >= totalPages || questionPagination.loading"
              @click="loadGeneratedQuestions(questionPagination.current + 1)"
            >
              下一�?➡️
            </button>
            <button
              class="btn btn-pagination"
              :disabled="questionPagination.current >= totalPages || questionPagination.loading"
              @click="loadGeneratedQuestions(totalPages)"
            >
              尾页
            </button>
          </div>

          <div class="pagination-jump">
            跳转�?            <input
              type="number"
              v-model.number="jumpPagePending"
              :min="1"
              :max="totalPages"
              class="jump-input"
              @keyup.enter="handleJumpPagePending"
              placeholder="页码"
            />
            �?            <button class="btn btn-pagination btn-jump" @click="handleJumpPagePending" :disabled="questionPagination.loading">
              跳转
            </button>
          </div>
        </div>
      </div>
    </div>

    <!-- ========== 题库管理面板 ========== -->
    <div v-show="activeTab === 'bank'" class="exam-container">
      <div class="exam-header" style="background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%);">
        <h1 class="exam-title">📚 题库管理中心</h1>
        <div class="user-info"><span>�?{{ questions.length }} 道题�?/span></div>
      </div>

      <div class="stats-row">
        <div class="stat-card">
          <div class="stat-icon">📝</div>
          <div class="stat-info">
            <div class="stat-value">{{ typeStats.single }}</div>
            <div class="stat-label">单选题</div>
          </div>
        </div>
        <div class="stat-card">
          <div class="stat-icon">�?/div>
          <div class="stat-info">
            <div class="stat-value">{{ typeStats.multiple }}</div>
            <div class="stat-label">多选题</div>
          </div>
        </div>
        <div class="stat-card">
          <div class="stat-icon">⚖️</div>
          <div class="stat-info">
            <div class="stat-value">{{ typeStats.judgment }}</div>
            <div class="stat-label">判断�?/div>
          </div>
        </div>
        <div class="stat-card">
          <div class="stat-icon">✏️</div>
          <div class="stat-info">
            <div class="stat-value">{{ typeStats.essay }}</div>
            <div class="stat-label">简答题</div>
          </div>
        </div>
        <div class="stat-card stat-card-total">
          <div class="stat-icon">📊</div>
          <div class="stat-info">
            <div class="stat-value">{{ typeStats.total }}</div>
            <div class="stat-label">合计</div>
          </div>
        </div>
      </div>

      <div class="exam-card">
        <div class="card-header">
          <h2>🔍 题目筛�?/h2>
          <div class="header-actions">
            <button class="btn btn-primary btn-sm" @click="triggerImportFile">📥 批量导入</button>
            <button class="btn btn-secondary btn-sm" @click="handleBatchExport">📤 批量导出</button>
          </div>
        </div>
        <div class="filter-row">
          <div class="filter-group">
            <label class="filter-label">制度文件</label>
            <FileSelector v-model="bankFilter.doc" :show-all-option="true" placeholder="筛选制度文�?.." />
          </div>
          <div class="filter-group">
            <label class="filter-label">题型</label>
            <select v-model="bankFilter.type" class="form-select filter-select">
              <option value="all">全部题型</option>
              <option value="single">单选题</option>
              <option value="multiple">多选题</option>
              <option value="judgment">判断�?/option>
              <option value="essay">简答题</option>
            </select>
          </div>
          <div class="filter-group">
            <label class="filter-label">难度</label>
            <select v-model="bankFilter.difficulty" class="form-select filter-select">
              <option value="all">全部难度</option>
              <option value="easy">简�?/option>
              <option value="medium">中等</option>
              <option value="hard">困难</option>
            </select>
          </div>
          <div class="filter-group">
            <label class="filter-label">状�?/label>
            <select v-model="bankFilter.status" class="form-select filter-select">
              <option value="all">全部状�?/option>
              <option value="approved">已审�?/option>
              <option value="pending">待审�?/option>
              <option value="rejected">禁用</option>
            </select>
          </div>
        </div>
      </div>

      <div class="exam-card">
        <div class="card-header">
          <h2>📋 题目列表</h2>
          <span class="status-badge status-info">�?{{ filteredBankQuestions.length }} �?/span>
        </div>
        <div class="bank-list">
          <div v-for="record in pagedBankQuestions" :key="record.id" class="bank-item">
            <div class="bank-item-main">
              <div class="bank-item-left">
                <span :class="['type-badge', record.type]">{{ record.type === 'single' ? '单�? : record.type === 'multiple' ? '多�? : record.type === 'judgment' ? '判断' : '简�? }}</span>
                <span :class="['diff-badge', record.difficulty]">{{ record.difficulty === 'easy' ? '简�? : record.difficulty === 'medium' ? '中等' : record.difficulty === 'hard' ? '困难' : record.difficulty }}</span>
              </div>
              <div class="bank-item-content">
                <div class="bank-question-text">{{ record.stem }}</div>
                <div class="bank-meta">
                  <span class="meta-tag">📄 {{ record.doc }}</span>
                  <span :class="['status-dot', record.status]">{{ record.status === 'approved' ? '�?已审�? : record.status === 'pending' ? '�?待审�? : '�?禁用' }}</span>
                  <button class="btn-expand-toggle" @click="record.expanded = !record.expanded">
                    {{ record.expanded ? '收起 �? : '展开 �? }}
                  </button>
                </div>
                <transition name="expand-answer">
                  <div v-if="record.expanded" class="bank-answer-section">
                    <div class="bank-answer-item" v-if="record.answer">
                      <span class="bank-answer-label">�?参考答案：</span>
                      <span class="bank-answer-content">{{ record.answer }}</span>
                    </div>
                    <div class="bank-answer-item" v-if="record.explanation">
                      <span class="bank-answer-label">💡 解析�?/span>
                      <span class="bank-answer-content">{{ record.explanation }}</span>
                    </div>
                  </div>
                </transition>
              </div>
              <div class="bank-item-actions">
                <button class="btn btn-primary btn-xs" @click="handleEditBankQuestion(record)">编辑</button>
                <button class="btn btn-danger btn-xs" @click="message.warning('题目已删�?)">删除</button>
              </div>
            </div>
          </div>
        </div>

        <!-- 题库管理分页控件 -->
        <div v-if="filteredBankQuestions.length > bankPageSize" class="pagination-wrapper">
          <div class="pagination-info">
            �?<strong>{{ filteredBankQuestions.length }}</strong> 条，
            �?<strong>{{ (bankCurrentPage - 1) * bankPageSize + 1 }}-{{ Math.min(bankCurrentPage * bankPageSize, filteredBankQuestions.length) }}</strong> �?          </div>

          <div class="pagination-controls">
            <button class="btn btn-pagination" :disabled="bankCurrentPage <= 1" @click="bankCurrentPage = 1">首页</button>
            <button class="btn btn-pagination" :disabled="bankCurrentPage <= 1" @click="bankCurrentPage--">⬅️ 上一�?/button>
            <div class="pagination-pages">
              <button
                v-for="page in getVisiblePages(bankCurrentPage, bankTotalPages)"
                :key="'bp-' + page"
                :class="['btn', 'btn-page', { active: page === bankCurrentPage }]"
                :disabled="page === bankCurrentPage || page === '...'"
                @click="page !== bankCurrentPage && page !== '...' && (bankCurrentPage = page)"
              >
                {{ page }}
              </button>
            </div>
            <button class="btn btn-pagination" :disabled="bankCurrentPage >= bankTotalPages" @click="bankCurrentPage++">下一�?➡️</button>
            <button class="btn btn-pagination" :disabled="bankCurrentPage >= bankTotalPages" @click="bankCurrentPage = bankTotalPages">尾页</button>
          </div>

          <div class="pagination-jump">
            跳转�?            <input type="number" v-model.number="jumpPageBank" :min="1" :max="bankTotalPages" class="jump-input" @keyup.enter="handleJumpPageBank" placeholder="页码" />
            �?            <button class="btn btn-pagination btn-jump" @click="handleJumpPageBank">跳转</button>
          </div>
        </div>
      </div>
    </div>

    <!-- ========== 智能组卷面板 ========== -->
    <div v-show="activeTab === 'paper'" class="exam-container">
      <div class="exam-header" style="background: linear-gradient(135deg, #fa709a 0%, #fee140 100%);">
        <h1 class="exam-title">📋 智能组卷系统</h1>
        <div class="user-info"><span>�?{{ papers.length }} 套试�?/span></div>
      </div>

      <div class="stats-row">
        <div class="stat-card">
          <div class="stat-icon">📋</div>
          <div class="stat-info"><div class="stat-value">{{ papers.length }}</div><div class="stat-label">试卷总数</div></div>
        </div>
        <div class="stat-card">
          <div class="stat-icon">�?/div>
          <div class="stat-info"><div class="stat-value">{{ papers.filter(p => p.status === 'published').length }}</div><div class="stat-label">已发�?/div></div>
        </div>
        <div class="stat-card">
          <div class="stat-icon">📝</div>
          <div class="stat-info"><div class="stat-value">{{ papers.filter(p => p.status === 'draft').length }}</div><div class="stat-label">草稿</div></div>
        </div>
      </div>

      <div class="exam-card">
        <div class="card-header"><h2>🤖 试卷配置</h2></div>
        <div class="form-row">
          <div class="form-group">
            <label class="form-label">试卷名称</label>
            <input type="text" v-model="paperConfig.name" class="form-input" placeholder="请输入试卷名�? />
          </div>
          <div class="form-group">
            <label class="form-label">关联制度</label>
            <FileSelector v-model="paperConfig.doc" placeholder="选择关联的制度文�?.." />
          </div>
        </div>
        <div class="form-group">
          <label class="form-label">题目分布设置</label>
          <div class="config-grid">
            <div class="config-item"><span class="config-label">单选题数量</span><input type="number" v-model.number="paperConfig.singleCount" min="0" class="form-input config-input" /></div>
            <div class="config-item"><span class="config-label">多选题数量</span><input type="number" v-model.number="paperConfig.multipleCount" min="0" class="form-input config-input" /></div>
            <div class="config-item"><span class="config-label">判断题数�?/span><input type="number" v-model.number="paperConfig.judgmentCount" min="0" class="form-input config-input" /></div>
            <div class="config-item"><span class="config-label">简答题数量</span><input type="number" v-model.number="paperConfig.essayCount" min="0" class="form-input config-input" /></div>
          </div>
        </div>
        <div class="form-group">
          <label class="form-label">难度比例分配�?�?/label>
          <div class="difficulty-bars">
            <div class="difficulty-bar-item">
              <span class="bar-label easy">简�?/span>
              <div class="bar-track"><div class="bar-fill easy" :style="{ width: paperConfig.easyPercent + '%' }"></div></div>
              <input type="number" v-model.number="paperConfig.easyPercent" min="0" max="100" class="bar-input" /><span class="bar-unit">%</span>
            </div>
            <div class="difficulty-bar-item">
              <span class="bar-label medium">中等</span>
              <div class="bar-track"><div class="bar-fill medium" :style="{ width: paperConfig.mediumPercent + '%' }"></div></div>
              <input type="number" v-model.number="paperConfig.mediumPercent" min="0" max="100" class="bar-input" /><span class="bar-unit">%</span>
            </div>
            <div class="difficulty-bar-item">
              <span class="bar-label hard">困难</span>
              <div class="bar-track"><div class="bar-fill hard" :style="{ width: paperConfig.hardPercent + '%' }"></div></div>
              <input type="number" v-model.number="paperConfig.hardPercent" min="0" max="100" class="bar-input" /><span class="bar-unit">%</span>
            </div>
          </div>
        </div>
        <div class="form-row">
          <div class="form-group">
            <label class="form-label">考试时长（分钟）</label>
            <input type="number" v-model.number="paperConfig.duration" min="1" class="form-input" />
          </div>
        </div>
        <div class="form-actions">
          <button class="btn btn-primary" @click="handleGeneratePaper">🚀 生成试卷</button>
          <button class="btn btn-success" @click="message.success('试卷模板保存成功�?)">💾 保存模板</button>
        </div>
      </div>

      <!-- 📝 草稿箱（仅当有未发布的本地草稿时显示�?-->
      <div v-if="localDraftPapers.length > 0" class="exam-card draft-card">
        <div class="card-header">
          <h2>📝 草稿�?/h2>
          <span class="draft-hint">
            <a-tooltip title="草稿保存在浏览器本地，关闭标签页后会自动清除。如需长期保存，请点击「发布」按钮�?>
              <span class="hint-icon">ℹ️</span>
              本地临时存储 · 关闭标签页后清除
            </a-tooltip>
          </span>
        </div>
        <div class="paper-list">
          <div v-for="record in localDraftPapers" :key="record.id" class="paper-item">
            <div class="paper-icon">📄</div>
            <div class="paper-info">
              <div class="paper-name">{{ record.name }}</div>
              <div class="paper-meta">
                <span class="meta-tag">📄 {{ record.doc || '通用组卷' }}</span>
                <span class="meta-tag">📝 {{ record.questions }} �?/span>
                <span class="meta-tag">⏱️ {{ record.duration }} 分钟</span>
                <span class="meta-tag status-warning">�?草稿</span>
              </div>
            </div>
            <div class="paper-actions">
              <button
                class="btn btn-primary btn-xs"
                :class="{ 'btn-loading': isViewLoading && currentViewingId === (record.paperId || record.id) }"
                @click="handleViewPaper(record)"
                :disabled="isViewLoading">
                <template v-if="isViewLoading && currentViewingId === (record.paperId || record.id)">
                  <span class="loading-dots">
                    <span class="dot"></span>
                    <span class="dot"></span>
                    <span class="dot"></span>
                  </span>
                </template>
                <template v-else>
                  <span>查看</span>
                </template>
              </button>
              <button class="btn btn-warning btn-xs" @click="openEditPaperModal(record)">编辑</button>
              <button class="btn btn-success btn-xs" @click="handleOpenPublishModal(record)">发布</button>
              <button class="btn btn-danger btn-xs" @click="removeDraftPaper(record.id)">删除</button>
            </div>
          </div>
        </div>
        
        <div class="draft-footer">
          <span class="draft-warning">⚠️</span>
          <span>草稿仅保存在当前浏览器会话中，关闭标签页后将自动清除。建议及时发布重要试卷�?/span>
        </div>
      </div>

      <!-- 📋 已发布试�?-->
      <div class="exam-card">
        <div class="card-header" style="cursor: pointer;" @click="togglePublishedList">
          <h2>📋 已发布试�?/h2>
          <span class="status-badge status-info">{{ showPublishedList ? '收起' : '展开' }}</span>
        </div>
        <div v-if="showPublishedList" class="paper-list">
          <!-- 加载动画 -->
          <div v-if="isLoadingPublished" class="loading-state">
            <a-spin size="large" />
            <p class="loading-text">正在加载已发布试�?..</p>
          </div>
          <!-- 空状�?-->
          <div v-else-if="publishedPapers.length === 0" class="empty-state">
            <div class="empty-icon">📭</div>
            <p>暂无已发布的试卷</p>
            <p class="empty-hint">生成并发布试卷后，可在此处查�?/p>
          </div>
          <!-- 试卷列表 -->
          <template v-else>
            <div v-for="record in publishedPapers" :key="record.paperId || record.id" class="paper-item">
              <div class="paper-icon">📋</div>
              <div class="paper-info">
                <div class="paper-name">{{ record.title || record.paperTitle || record.paper_title || record.name || '未命名试�? }}</div>
                <div class="paper-meta">
                  <span class="meta-tag">📝 {{ record.questionCount || record.questions?.length || '?' }} �?/span>
                  <span class="meta-tag">📊 {{ formatScore(record.totalScore) }} �?/span>
                  <span class="meta-tag status-success">�?已发�?/span>
                </div>
              </div>
              <div class="paper-actions">
                <button
                  class="btn btn-primary btn-xs"
                  :class="{ 'btn-loading': isViewLoading && currentViewingId === (record.paperId || record.id) }"
                  @click="handleViewPaper(record)"
                  :disabled="isViewLoading">
                  <template v-if="isViewLoading && currentViewingId === (record.paperId || record.id)">
                    <span class="loading-dots">
                      <span class="dot"></span>
                      <span class="dot"></span>
                      <span class="dot"></span>
                    </span>
                  </template>
                  <template v-else>
                    <span>查看</span>
                  </template>
                </button>
              </div>
            </div>
          </template>
        </div>
      </div>
    </div>
    
    <!-- ========== 互动训练面板 ========== -->
    <div v-show="activeTab === 'train'" class="exam-container">
      <!-- 训练标题�?-->
      <div class="exam-header">
        <h1 class="exam-title">🎯 互动式训练系�?/h1>
        <div class="user-info">
          <span>👤 当前用户</span>
        </div>
      </div>

      <!-- 训练统计 -->
      <div class="stats-row">
        <div class="stat-card">
          <div class="stat-icon">🏆</div>
          <div class="stat-info">
            <div class="stat-value">{{ trainingData.checkpoints.filter(c => c.status === 'completed').length }}</div>
            <div class="stat-label">已通关</div>
          </div>
        </div>
        <div class="stat-card">
          <div class="stat-icon">📅</div>
          <div class="stat-info">
            <div class="stat-value">{{ dailyExerciseCount }}</div>
            <div class="stat-label">今日练习</div>
          </div>
        </div>
        <div class="stat-card">
          <div class="stat-icon">�?/div>
          <div class="stat-info">
            <div class="stat-value">{{ trainingData.wrongQuestions.length }}</div>
            <div class="stat-label">错题数量</div>
          </div>
        </div>
      </div>

      <!-- 训练标签�?-->
      <div class="train-tabs">
        <button 
          :class="['train-tab', { active: trainTab === 'checkpoint' }]" 
          @click="trainTab = 'checkpoint'"
        >
          🗺�?闯关模式
        </button>
        <button 
          :class="['train-tab', { active: trainTab === 'daily' }]" 
          @click="trainTab = 'daily'"
        >
          📝 每日一�?        </button>
        <button 
          :class="['train-tab', { active: trainTab === 'wrong' }]" 
          @click="trainTab = 'wrong'"
        >
          📚 错题�?        </button>
      </div>

      <!-- 闯关模式面板 -->
      <div v-if="trainTab === 'checkpoint'" class="exam-card">
        <div class="card-header">
          <h2>🗺�?闯关地图</h2>
          <span class="status-badge status-info">�?{{ trainingData.checkpoints.length }} �?/span>
        </div>
        
        <div class="checkpoint-map">
          <div 
            v-for="(checkpoint, index) in trainingData.checkpoints" 
            :key="checkpoint.id" 
            :class="['checkpoint-item', checkpoint.status]"
          >
            <div class="checkpoint-node" @click="handleStartCheckpoint(checkpoint)">
              <div class="node-circle">
                {{ checkpoint.id }}
              </div>
              <div class="node-info">
                <div class="node-name">{{ checkpoint.name }}</div>
                <div :class="['node-status', checkpoint.status]">
                  {{ checkpoint.status === 'completed' ? `�?得分�?{checkpoint.score}分` : 
                     checkpoint.status === 'in-progress' ? '🔄 进行�?..' : '�?未开�? }}
                </div>
              </div>
              <button 
                :class="['btn', checkpoint.status === 'completed' || checkpoint.status === 'in-progress' ? 'btn-primary' : 'btn-secondary']"
                :disabled="checkpoint.status === 'pending'"
                @click.stop="handleStartCheckpoint(checkpoint)"
              >
                {{ checkpoint.status === 'completed' ? '重新挑战' : checkpoint.status === 'in-progress' ? '继续挑战' : '🔒 未解�? }}
              </button>
            </div>
            <div v-if="index < trainingData.checkpoints.length - 1" class="checkpoint-line"></div>
          </div>
        </div>
      </div>

      <!-- 每日一练面�?-->
      <div v-if="trainTab === 'daily'" class="exam-card">
        <div class="card-header">
          <h2>📝 每日一练（{{ generatedQuestions.length }} 题）</h2>
          <div class="header-actions">
            <button v-if="generatedQuestions.length > 0" class="btn btn-primary btn-sm" @click="handleSubmitDailyExercise" :disabled="isGrading">
              {{ isGrading ? '�?批改�?..' : '�?提交全部答案' }}
            </button>
          </div>
        </div>

        <div v-if="generatedQuestions.length > 0" class="questions-list">
          <div v-for="(q, index) in generatedQuestions" :key="index" :class="['question-item', 'answer-mode', { 'answered': q.studentAnswer != null && q.studentAnswer !== '' }]">
            <div class="question-header">
              <span :class="['type-badge', q.typeClass]">
                {{ q.typeLabel }}
              </span>
              <span class="question-number">�?{{ index + 1 }} �?/span>
              <span v-if="q.studentAnswer != null && q.studentAnswer !== ''" class="answered-badge">�?已答</span>
            </div>
            
            <div class="question-content">
              {{ q.stem }}
            </div>

            <div v-if="q.type === 'single_choice' && q.options?.length" class="question-options answer-options">
              <label v-for="opt in q.options" :key="opt.key" class="option-radio" :class="{ selected: q.studentAnswer === opt.key }">
                <input type="radio" :name="`daily${q.id}`" :value="opt.key" v-model="q.studentAnswer" />
                <span class="option-letter">{{ opt.key }}.</span>
                <span class="option-text">{{ opt.content }}</span>
              </label>
            </div>

            <div v-if="q.type === 'multiple_choice' && q.options?.length" class="question-options answer-options">
              <label v-for="opt in q.options" :key="opt.key" class="option-checkbox" :class="{ selected: isMultiSelected(q, opt.key) }">
                <input type="checkbox" :value="opt.key" @change="toggleMultiAnswer(q, opt.key)" :checked="isMultiSelected(q, opt.key)" />
                <span class="option-letter">{{ opt.key }}.</span>
                <span class="option-text">{{ opt.content }}</span>
              </label>
            </div>

            <div v-if="q.type === 'true_false'" class="question-options answer-options">
              <label class="option-radio" :class="{ selected: q.studentAnswer === 'true' }">
                <input type="radio" :name="`daily${q.id}`" value="true" v-model="q.studentAnswer" />
                <span class="option-letter">A.</span>
                <span class="option-text">正确</span>
              </label>
              <label class="option-radio" :class="{ selected: q.studentAnswer === 'false' }">
                <input type="radio" :name="`daily${q.id}`" value="false" v-model="q.studentAnswer" />
                <span class="option-letter">B.</span>
                <span class="option-text">错误</span>
              </label>
            </div>

            <div v-if="q.type === 'fill_blank'" class="fill-blank-answer">
              <label class="form-label">请填写答案：</label>
              <input type="text" v-model="q.studentAnswer" class="form-input" placeholder="请输入答�? />
            </div>

            <div v-if="q.type === 'subjective'" class="subjective-answer">
              <label class="form-label">请作答：</label>
              <textarea v-model="q.studentAnswer" class="form-input essay-textarea" rows="4" placeholder="请输入您的答�?.."></textarea>
              <span class="char-count">{{ (q.studentAnswer || '').length }} �?/span>
            </div>
          </div>
        </div>

        <div v-else class="empty-state">
          <div class="empty-icon">📝</div>
          <div class="empty-text">暂无练习题目</div>
          <div class="empty-hint">请先到「AI出题」生成题目，或切换到「闯关模式」开始训�?/div>
        </div>
      </div>

      <!-- 错题本面�?-->
      <div v-if="trainTab === 'wrong'" class="exam-card">
        <div class="card-header">
          <h2>📚 我的错题�?/h2>
          <span class="status-badge status-warning">�?{{ trainingData.wrongQuestions.length }} 道错�?/span>
        </div>

        <div class="questions-list" v-if="trainingData.wrongQuestions.length > 0">
          <div v-for="item in trainingData.wrongQuestions" :key="item.id" class="question-item wrong-item">
            <div class="question-header">
              <span :class="['type-badge', item.type]">{{ item.typeLabel || formatQuestionType(item.type) }}</span>
              <div class="question-actions">
                <button class="btn btn-primary btn-xs" @click="handleRetryWrongQuestion(item)">重练</button>
                <button class="btn btn-success btn-xs" @click="handleViewWrongQuestion(item)">查看解析</button>
              </div>
            </div>

            <div class="question-content">{{ item.content }}</div>

            <div class="wrong-meta">
              <div class="meta-item">
                <span class="meta-label">来源�?/span>
                <span class="meta-value">{{ item.doc || '未知' }}</span>
              </div>
              <div class="meta-item">
                <span class="meta-label">得分�?/span>
                <span class="meta-value error-count">{{ item.score || 0 }}/{{ item.maxScore || 5 }} �?/span>
              </div>
              <div v-if="item.feedback" class="meta-item">
                <span class="meta-label">反馈�?/span>
                <span class="meta-value">{{ item.feedback }}</span>
              </div>
            </div>
          </div>
        </div>

        <div v-else class="empty-state">
          <div class="empty-icon">🎉</div>
          <div class="empty-text">太棒了！目前没有错题</div>
          <div class="empty-hint">继续保持，加油学习！</div>
        </div>
      </div>
    </div>

    <!-- ========== 试卷考核面板 ========== -->
    <div v-show="activeTab === 'exam'" class="exam-container">
      <div class="exam-header">
        <h1 class="exam-title">📝 试卷考核</h1>
        <div class="user-info">
          <span>👤 当前用户</span>
        </div>
      </div>

      <!-- 子标签栏 -->
      <div class="sub-tabs-bar">
        <button :class="['sub-tab', { active: examTab === 'pending' }]" @click="examTab = 'pending'">
          📋 待考试<span v-if="pendingPapers.length" class="tab-badge">{{ pendingPapers.length }}</span>
        </button>
        <button :class="['sub-tab', { active: examTab === 'taking' }]" @click="examTab = 'taking'" :disabled="!currentTakingPaper">
          ✏️ 答题�?        </button>
        <button :class="['sub-tab', { active: examTab === 'results' }]" @click="examTab = 'results'">
          📊 成绩�?span v-if="completedResults.length" class="tab-badge">{{ completedResults.length }}</span>
        </button>
        <button :class="['sub-tab', { active: examTab === 'wrong' }]" @click="loadExamWrongQuestions()">
          �?错题�?        </button>
        <button :class="['sub-tab', { active: examTab === 'selftest' }]" @click="examTab = 'selftest'">
          🔧 自测组卷
        </button>
      </div>

      <!-- ===== 待考试列表 ===== -->
      <div v-if="examTab === 'pending'" class="exam-card">
        <div class="card-header">
          <h2>📋 待考试列表</h2>
          <a-button type="primary" size="small" @click="loadPendingPapers()" :loading="isLoadingPapers">
            刷新
          </a-button>
        </div>
        <div v-if="isLoadingPapers" class="loading-state">
          <a-spin size="large" />
          <p>正在加载试卷列表...</p>
        </div>
        <div v-else-if="pendingPapers.length > 0" class="papers-grid">
          <div v-for="paper in pendingPapers" :key="paper.paperId || paper.recordId || paper.id" class="paper-card" @click="startTakingExam(paper)">
            <div class="paper-card-header">
              <span :class="['paper-status-badge', paper.status === 'not_started' ? 'status-pending' : 'status-active']">{{ paper.status === 'not_started' ? '待考试' : (paper.status === 'in_progress' ? '进行�? : '已完�?) }}</span>
              <span class="paper-score">{{ formatScore(paper.totalScore) }}�?/span>
            </div>
            <h3 class="paper-card-title">{{ paper.paperTitle || paper.paper_title || paper.name || paper.title || '未命名试�? }}</h3>
            <div class="paper-card-meta">
              <span>📝 {{ paper.questions?.length || paper.questionCount || paper.question_count || (paper.status === 'not_started' ? '�? : '?') }} �?/span>
              <span>📅 {{ formatDate(paper.startTime || paper.generatedAt || paper.generated_at) }}</span>
              <span>📂 {{ paper.permissionScope || paper.permission_scope || '本部�? }}</span>
            </div>
            <div class="paper-card-action">
              <span class="action-hint">点击开始答�?�?/span>
            </div>
          </div>
        </div>
        <div v-else class="empty-state">
          <div class="empty-icon">📭</div>
          <div class="empty-text">暂无待考试试卷</div>
          <div class="empty-hint">请等待管理员发布试卷，或使用「自测组卷」自行练�?/div>
          <button class="btn btn-primary" style="margin-top:12px;" @click="examTab = 'selftest'">前往自测组卷</button>
        </div>
      </div>

      <!-- ===== 答题页面 ===== -->
      <div v-if="examTab === 'taking' && currentTakingPaper" class="exam-taking-container">
        <div class="taking-header">
          <button class="btn btn-outline btn-sm" @click="exitTakingExam()">
            �?返回
          </button>
          <div class="taking-title">
            <span>📝 {{ currentTakingPaper.name || currentTakingPaper.paperTitle || '试卷答题' }}</span>
          </div>
          <div class="taking-progress">
            �?{{ currentQuestionIndex + 1 }} / {{ currentTakingPaper.questionList?.length || 0 }} �?          </div>
        </div>

        <div class="taking-layout">
          <!-- 左侧���⿨-->
          <div class="answer-card-sidebar">
            <div class="answer-card-header">
              <span class="answer-card-title">���⿨/span>
              <span class="answer-card-stats">{{ answeredCount }} / {{ totalQuestions }} ���Ѵ�/span>
            </div>
            <div class="answer-card-grid">
              <div v-for="(q, idx) in (currentTakingPaper.questionList || [])" :key="idx"
                   :class="['answer-card-item', {
                     answered: !!takingAnswers[q.questionId],
                     active: idx === currentQuestionIndex
                   }]"
                   @click="currentQuestionIndex = idx">
                <span class="card-item-num">{{ idx + 1 }}</span>
                <span v-if="!!takingAnswers[q.questionId]" class="card-check-mark">�?/span>
              </div>
            </div>
          </div>

          <!-- 右侧题目区域 -->
          <div class="taking-content-area">
            <div class="taking-body" v-if="currentQuestion">
              <div class="question-type-tag">
                <span :class="['type-badge', currentQuestion.type]">{{ currentQuestion.typeLabel || formatQuestionType(currentQuestion.type) }}</span>
                <span class="question-score">�?{{ currentQuestion.score || 5 }} �?/span>
              </div>

              <div class="question-stem">{{ currentQuestionIndex + 1 }}. {{ currentQuestion.stem }}</div>

              <!-- 单选题 -->
              <div v-if="currentQuestion.type === 'single_choice' || currentQuestion.type === 'single'" class="options-list">
                <label v-for="(opt, idx) in currentQuestion.options" :key="idx"
                       :class="['option-item', { selected: takingAnswers[currentQuestion.questionId] === opt.key }]">
                  <input type="radio" :name="'q_' + currentQuestion.questionId" :value="opt.key"
                         v-model="takingAnswers[currentQuestion.questionId]">
                  <span class="option-key">{{ opt.key }}</span>
                  <span class="option-text">{{ opt.content }}</span>
                </label>
              </div>

              <!-- 多选题 -->
              <div v-else-if="currentQuestion.type === 'multiple_choice' || currentQuestion.type === 'multiple'" class="options-list">
                <label v-for="(opt, idx) in currentQuestion.options" :key="idx"
                       :class="['option-item multi', { selected: (takingAnswers[currentQuestion.questionId] || []).includes(opt.key) }]">
                  <input type="checkbox" :value="opt.key"
                         @change="toggleMultiAnswerForTaking(currentQuestion.questionId, opt.key)">
                  <span class="option-key">{{ opt.key }}</span>
                  <span class="option-text">{{ opt.content }}</span>
                </label>
              </div>

              <!-- 判断�?-->
              <div v-else-if="currentQuestion.type === 'true_false' || currentQuestion.type === 'judgment'" class="options-list options-inline">
                <label :class="['option-item', { selected: takingAnswers[currentQuestion.questionId] === 'true' }]">
                  <input type="radio" :name="'q_' + currentQuestion.questionId" value="true"
                         v-model="takingAnswers[currentQuestion.questionId]">
                  <span>�?正确</span>
                </label>
                <label :class="['option-item', { selected: takingAnswers[currentQuestion.questionId] === 'false' }]">
                  <input type="radio" :name="'q_' + currentQuestion.questionId" value="false"
                         v-model="takingAnswers[currentQuestion.questionId]">
                  <span>�?错误</span>
                </label>
              </div>

              <!-- 填空�?-->
              <div v-else-if="currentQuestion.type === 'fill_blank'" class="fill-blank-area">
                <textarea v-model="takingAnswers[currentQuestion.questionId]" placeholder="请输入答�?.." rows="3"></textarea>
              </div>

              <!-- 简答题 -->
              <div v-else-if="currentQuestion.type === 'subjective' || currentQuestion.type === 'essay'" class="subjective-area">
                <textarea v-model="takingAnswers[currentQuestion.questionId]" placeholder="请详细作�?.." rows="6"></textarea>
              </div>
            </div>

            <div class="taking-footer">
              <button class="btn btn-outline" :disabled="currentQuestionIndex <= 0" @click="currentQuestionIndex--">
                �?上一�?              </button>
              <div class="footer-center-info">
                <span class="footer-question-num">{{ currentQuestionIndex + 1 }} / {{ currentTakingPaper.questionList?.length || 0 }}</span>
              </div>
              <button v-if="currentTakingPaper.questionList && currentQuestionIndex < currentTakingPaper.questionList.length - 1" class="btn btn-primary" @click="currentQuestionIndex++">
                下一�?�?              </button>
              <button v-else class="btn btn-success" @click="submitExamAnswers()" :disabled="isSubmitting">
                {{ isSubmitting ? '提交�?..' : '📤 提交试卷' }}
              </button>
            </div>
          </div>
        </div>
      </div>

      <div v-else-if="examTab === 'taking' && !currentTakingPaper" class="exam-card">
        <div class="empty-state">
          <div class="empty-icon">📝</div>
          <div class="empty-text">当前没有正在进行的答�?/div>
          <div class="empty-hint">从待考试列表选择一张试卷开始答�?/div>
          <button class="btn btn-primary" style="margin-top:12px;" @click="examTab = 'pending'">查看待考试</button>
        </div>
      </div>

      <!-- ===== 成绩�?===== -->
      <div v-if="examTab === 'results'" class="exam-card">
        <div class="card-header">
          <h2>📊 成绩�?/h2>
        </div>
        <div v-if="completedResults.length > 0">
          <div v-for="(result, rIdx) in completedResults" :key="rIdx" class="result-card">
            <div class="result-header">
              <h3>{{ result.paperName || result.paperTitle || '试卷' }}</h3>
              <div class="result-score-big">
                <span class="score-num">{{ result.totalScore || result.total_score || 0 }}</span>
                <span class="score-unit">/ {{ result.totalMaxScore || result.total_max_score || 100 }} �?/span>
              </div>
              <span :class="['rate-badge', getScoreRateClass(result.scoreRate || result.score_rate)]">
                得分�?{{ Math.round((result.scoreRate || result.score_rate || 0) * 100) }}%
              </span>
            </div>
            <div class="result-time">{{ result.completedAt || result.submittedAt || new Date().toLocaleString() }}</div>

            <div class="result-questions" v-if="result.results">
              <div v-for="(qr, qIdx) in result.results" :key="qIdx" :class="['result-question-row', qr.correct ? 'correct' : 'wrong']">
                <span class="rq-status">{{ qr.correct ? '�? : '�? }}</span>
                <span class="rq-index">第{{ qIdx + 1 }}�?/span>
                <span class="rq-score">{{ qr.score || 0 }}/{{ qr.maxScore || 5 }}�?/span>
                <span class="rq-feedback" v-if="!qr.correct && qr.feedback">{{ qr.feedback }}</span>
              </div>
            </div>
          </div>
        </div>
        <div v-else class="empty-state">
          <div class="empty-icon">📊</div>
          <div class="empty-text">暂无成绩记录</div>
          <div class="empty-hint">完成答题后这里会显示你的成绩�?/div>
        </div>
      </div>

      <!-- ===== 错题�?试卷考核) ===== -->
      <div v-if="examTab === 'wrong'" class="exam-card">
        <div class="card-header">
          <h2>�?错题本（试卷�?/h2>
          <a-button type="primary" size="small" @click="loadExamWrongQuestions()" :loading="isLoadingWrongQuestions">刷新</a-button>
        </div>
        <div v-if="isLoadingWrongQuestions" class="loading-state"><a-spin /><p>加载�?..</p></div>
        <div v-else-if="examWrongQuestions.length > 0" class="questions-list">
          <div v-for="(item, wIdx) in examWrongQuestions" :key="wIdx" class="question-item wrong-item">
            <div class="question-header">
              <span :class="['type-badge', item.question_type || item.type]">{{ item.typeLabel || formatQuestionType(item.question_type || item.type) }}</span>
              <div class="question-actions">
                <button class="btn btn-primary btn-xs" @click="redoExamWrongQuestion(item)">重做</button>
                <button class="btn btn-success btn-xs" @click="viewExamWrongDetail(item)">解析</button>
              </div>
            </div>
            <div class="question-content">{{ item.stem || item.content || item.question_content }}</div>
            <div class="wrong-meta">
              <span class="meta-label">得分：{{ item.student_score || item.score || 0 }}/{{ item.max_score || 5 }}�?/span>
              <span v-if="item.feedback" class="meta-label">反馈：{{ item.feedback }}</span>
            </div>
          </div>
        </div>
        <div v-else class="empty-state">
          <div class="empty-icon">🎉</div>
          <div class="empty-text">太棒了！目前没有错题</div>
        </div>
      </div>

      <!-- ===== 自测组卷 ===== -->
      <div v-if="examTab === 'selftest'" class="exam-card">
        <div class="card-header">
          <h2>🔧 自测组卷</h2>
        </div>
        <p class="section-desc">配置参数生成测试卷，生成后直接进入答题模式。生成的试卷仅用于自测练习，不会保存到系统中�?/p>

        <div class="form-group">
          <label class="form-label">单选题数量</label>
          <input type="number" v-model.number="selftestConfig.singleCount" min="0" max="50" class="form-input" />
        </div>
        <div class="form-group">
          <label class="form-label">多选题数量</label>
          <input type="number" v-model.number="selftestConfig.multipleCount" min="0" max="30" class="form-input" />
        </div>
        <div class="form-group">
          <label class="form-label">判断题数�?/label>
          <input type="number" v-model.number="selftestConfig.judgmentCount" min="0" max="30" class="form-input" />
        </div>
        <div class="form-group">
          <label class="form-label">简答题数量</label>
          <input type="number" v-model.number="selftestConfig.essayCount" min="0" max="10" class="form-input" />
        </div>
        <div class="form-group">
          <label class="form-label">难度等级</label>
          <div class="difficulty-selector">
            <button v-for="d in 5" :key="d" :class="['diff-btn', { active: selftestConfig.difficulty === d }]"
                    @click="selftestConfig.difficulty = d">{{ d }}</button>
            <span class="diff-label">{{ ['极简', '简�?, '中等', '较难', '困难'][selftestConfig.difficulty - 1] }}</span>
          </div>
        </div>

        <button class="btn btn-primary btn-block" style="margin-top:16px;" @click="startSelftest()" :disabled="isGeneratingSelftest">
          {{ isGeneratingSelftest ? '正在生成...' : '🚀 开始测�? }}
        </button>
      </div>
    </div>

    <!-- 弹窗组件 -->
    <a-modal
      :title="generateModalTitle"
      v-model:open="showGenerateModal"
      :footer="null"
      :closable="!isGenerating"
      :maskClosable="!isGenerating"
    >
      <div class="generate-modal-content">
        <div class="generate-spinner" v-if="isGenerating">
          <div class="spinner-ring"></div>
          <div class="spinner-icon">🤖</div>
        </div>
        <div class="generate-status-icon" v-else-if="generateError">�?/div>
        <div class="generate-status-icon" v-else>�?/div>
        <h3 class="generate-title">{{ generateStatusText }}</h3>
        <!-- 等待状态提示（替代原来的百分比进度条） -->
        <div v-if="isGenerating" class="waiting-indicator">
          <div class="waiting-dots">
            <span></span><span></span><span></span>
          </div>
        </div>
        <p class="generate-detail">{{ generateDetailText }}</p>
        <div v-if="!isGenerating && !generateError" class="generate-result-summary">
          <div class="result-item">
            <span class="result-label">生成题目</span>
            <span class="result-value success">{{ generatedQuestions.length }} �?/span>
          </div>
        </div>
        <button 
          v-if="!isGenerating" 
          class="btn btn-primary" 
          style="margin-top: 20px;"
          @click="showGenerateModal = false"
        >
          {{ generateError ? '关闭' : '查看结果' }}
        </button>
      </div>
    </a-modal>

    <!-- 发布试卷Modal -->
    <a-modal
      title="📢 发布试卷"
      :open="showPublishPaperModal"
      @cancel="showPublishPaperModal = false"
      width="640px"
      class="publish-paper-modal"
      destroyOnClose
    >
      <div v-if="publishingPaper" class="publish-modal-body">
        <div class="publish-paper-info">
          <div class="publish-paper-name">📄 {{ publishingPaper.name }}</div>
          <div class="publish-paper-stats">
            <span>📝 {{ publishingPaper.questions }} �?/span>
            <span>📊 {{ publishingPaper.totalScore || 100 }} �?/span>
            <span>⏱️ {{ publishingPaper.duration }} 分钟</span>
          </div>
        </div>

        <div class="publish-form-group">
          <label class="publish-label">
            <span v-if="isSystemAdmin">📂 发布到部�?/span>
            <span v-else>📂 所属部�?/span>
          </label>
          <div v-if="isSystemAdmin">
            <select v-model="selectedPublishDeptId" class="form-select" @change="handlePublishDeptChange">
              <option :value="null">-- 不限定部门（仅指定用户） --</option>
              <option v-for="dept in departmentList" :key="dept.id || dept.deptId" :value="dept.id || dept.deptId">
                {{ dept.deptName || dept.name }}
              </option>
            </select>
          </div>
          <div v-else class="publish-dept-display">
            <span class="dept-tag">{{ departmentList.find(d => (d.id || d.deptId) === currentUserDeptId.value)?.deptName || '本部�? }}</span>
          </div>
        </div>

        <div class="publish-form-group">
          <label class="publish-label">
            👤 用户范围
            <span class="publish-mode-toggle">
              <span
                class="publish-mode-option"
                :class="{ active: publishUserMode === 'include' }"
                @click="handlePublishModeChange('include')"
              >指定考试</span>
              <span
                class="publish-mode-option"
                :class="{ active: publishUserMode === 'exclude' }"
                @click="handlePublishModeChange('exclude')"
              >排除考试</span>
            </span>
          </label>
          <div class="publish-mode-hint">
            {{ publishUserModeHint }}
          </div>

          <!-- 用户选择区域 -->
          <div class="user-select-wrapper">
            <!-- 加载状�?-->
            <div v-if="publishUserLoading" class="publish-user-loading">
              <a-spin size="large" />
              <div class="loading-text">加载用户列表�?..</div>
            </div>

            <!-- 无数据状�?-->
            <div v-else-if="userList.length === 0" class="publish-user-empty">
              {{ selectedPublishDeptId ? '该部门暂无用�? : '请先选择部门后再选用�? }}
            </div>

            <!-- 正常选择界面 -->
            <template v-else>
              <!-- 顶部搜索�?-->
              <div class="user-select-toolbar">
                <div class="user-search-box">
                  <span class="search-icon">🔍</span>
                  <input
                    v-model="userSearchKeyword"
                    placeholder="搜索用户�?部门"
                    class="user-search-input"
                    type="text"
                  />
                  <span v-if="userSearchKeyword" class="clear-search" @click="userSearchKeyword = ''">�?/span>
                </div>
                <div class="user-select-actions">
                  <button
                    class="btn btn-default btn-sm"
                    @click="selectAllFiltered"
                    :disabled="isAllSelectedInFiltered || filteredUserList.length === 0"
                  >
                    全选当�?                  </button>
                  <button
                    class="btn btn-default btn-sm"
                    @click="deselectAllFiltered"
                    :disabled="!isSomeSelectedInFiltered || filteredUserList.length === 0"
                  >
                    取消全�?                  </button>
                  <button
                    class="btn btn-default btn-sm btn-danger"
                    @click="clearAllSelected"
                    :disabled="currentSelectedUserIds.length === 0"
                  >
                    清空选择
                  </button>
                </div>
              </div>

              <!-- 搜索结果计数 -->
              <div class="user-select-count">
                �?{{ filteredUserList.length }} �?                <template v-if="userSearchKeyword">（筛选结果）</template>
                <template v-if="userTotalPages > 1">，第 {{ userPage }} / {{ userTotalPages }} �?/template>
                ，已�?<span class="selected-count">{{ currentSelectedUserIds.length }}</span> �?              </div>

              <!-- 用户列表 -->
              <div class="user-list-grid">
                <div
                  v-for="user in currentPageUsers"
                  :key="user.id || user.userId"
                  class="user-item-card"
                  :class="{ 'user-item-selected': currentSelectedUserIds.includes(user.id || user.userId) }"
                  @click="toggleUserSelect(user)"
                >
                  <div class="user-checkbox">
                    <span class="checkbox-box" :class="{ 'checked': currentSelectedUserIds.includes(user.id || user.userId) }">
                      <span v-if="currentSelectedUserIds.includes(user.id || user.userId)">�?/span>
                    </span>
                  </div>
                  <div class="user-info">
                    <div class="user-name">{{ user.nickName || user.nickname || user.realName || user.username }}</div>
                    <div class="user-dept" v-if="user.deptName">{{ user.deptName }}</div>
                  </div>
                </div>
              </div>

              <!-- 分页控件 -->
              <div v-if="userTotalPages > 1" class="user-pagination-wrapper">
                <div class="user-pagination">
                  <!-- 上一�?-->
                  <button 
                    class="page-btn"
                    :disabled="userPage === 1"
                    @click="changeUserPage(userPage - 1)"
                  >
                    上一�?                  </button>
                  
                  <!-- 页码 -->
                  <template v-for="pageNum in userPageNumbers" :key="pageNum">
                    <button
                      v-if="pageNum !== '...'"
                      class="page-num"
                      :class="{ active: pageNum === userPage }"
                      @click="changeUserPage(pageNum)"
                    >
                      {{ pageNum }}
                    </button>
                    <span v-else class="page-ellipsis">...</span>
                  </template>
                  
                  <!-- 下一�?-->
                  <button 
                    class="page-btn"
                    :disabled="userPage === userTotalPages"
                    @click="changeUserPage(userPage + 1)"
                  >
                    下一�?                  </button>
                </div>
              </div>

              <!-- 已选用户标签展�?-->
              <div v-if="selectedUsers.length > 0" class="selected-users-section">
                <div class="selected-users-header">
                  <span class="selected-users-title">已选用户（{{ selectedUsers.length }}人）</span>
                </div>
                <div class="selected-users-tags">
                  <a-tag v-for="user in selectedUsers" closable @close="removeSelectedUser(user)">
                    {{ user.nickName || user.nickname || user.realName || user.username }}
                    <template v-if="user.deptName" class="tag-dept">({{ user.deptName }})</template>
                  </a-tag>
                </div>
              </div>
            </template>
          </div>
        </div>

        <div class="publish-form-group">
          <label class="publish-label">�?截止时间（可选）</label>
          <input type="datetime-local" v-model="publishDeadline" class="form-input" />
        </div>

        <div class="publish-form-group publish-summary">
          <span class="publish-summary-text">
            📋 发布后，请选择发布范围
          </span>
        </div>
      </div>

      <template #footer>
        <a-button @click="showPublishPaperModal = false">取消</a-button>
        <a-button type="primary" :loading="publishLoading" @click="handleConfirmPublish">
          {{ publishLoading ? '发布�?..' : '确认发布' }}
        </a-button>
      </template>
    </a-modal>

    <a-modal
      title="✏️ 编辑试卷"
      :key="'ep-modal-' + showEditPaperModal"
      v-model:open="showEditPaperModal"
      @cancel="handleCancelEditPaper"
      width="1080px"
      class="edit-paper-modal"
      :maskClosable="false"
      :closable="true"
      destroyOnClose
    >
      <div v-if="editingPaper" class="ep-layout">
        <!-- 左侧：题目列�?-->
        <div class="ep-sidebar">
          <div class="ep-sidebar-header">
            <input v-model="editingPaper.name" class="form-input ep-name-input" placeholder="试卷名称" />
            <span class="ep-total-score">{{ computedEditTotalScore }}�?/span>
          </div>

          <div class="ep-question-list">
            <div
              v-for="(q, qIdx) in editingPaper.questionList" :key="qIdx"
              :class="['ep-q-item', { active: selectedEditIndex === qIdx }]"
              @click="selectedEditIndex = qIdx"
            >
              <div class="ep-q-badge" :class="'type-' + (q.type || 'single_choice')">{{ qIdx + 1 }}</div>
              <div class="ep-q-info">
                <div class="ep-q-type">{{ formatQuestionType(q.type) }}</div>
                <div class="ep-q-stem-preview">{{ q.stem || '(未填写题�?' }}</div>
              </div>
              <div class="ep-q-score">{{ q.score || 5 }}�?/div>
            </div>
          </div>

          <button class="btn btn-primary btn-block ep-add-btn" @click="handleAddQuestionAndSelect">
            + 添加题目
          </button>
        </div>

        <!-- 右侧：编辑器 -->
        <div class="ep-editor" v-if="currentEditQuestion">
          <div class="ep-editor-header">
            <select v-model="currentEditQuestion.type" class="form-select type-select-lg">
              <option value="single_choice">单选题</option>
              <option value="multiple_choice">多选题</option>
              <option value="true_false">判断�?/option>
              <option value="fill_blank">填空�?/option>
              <option value="subjective">简答题</option>
            </select>
            <div class="ep-score-wrap">
              <label>分�?/label>
              <input type="number" v-model.number="currentEditQuestion.score" min="1" max="50" class="form-input score-input-lg" />
            </div>
          </div>

          <div class="ep-editor-body">
            <div class="ep-field">
              <label>题干内容</label>
              <textarea v-model="currentEditQuestion.stem" rows="6" class="form-input ep-stem-area" placeholder="输入题目内容..."></textarea>
            </div>

            <!-- 单�?多选选项 -->
            <div v-if="isChoiceType(currentEditQuestion.type)" class="ep-field">
              <label>选项
                <span class="ep-hint">(点击圆点设为正确答案)</span>
              </label>
              <div class="ep-options-grid">
                <div v-for="(opt, oIdx) in currentEditQuestion.options" :key="oIdx" class="ep-opt-row">
                  <span class="ep-opt-key">{{ opt.key }}</span>
                  <input type="text" v-model="opt.content" class="form-input ep-opt-content" placeholder="选项内容" />
                  <label v-if="currentEditQuestion.type === 'single_choice'" class="ep-opt-radio">
                    <input type="radio" :name="'ec_' + selectedEditIndex" :value="opt.key" v-model="currentEditQuestion.answer" />
                  </label>
                  <label v-else class="ep-opt-check">
                    <input type="checkbox" :value="opt.key"
                           :checked="(Array.isArray(currentEditQuestion.answer) ? currentEditQuestion.answer : []).includes(opt.key)"
                           @change="toggleEditMultiAnswerForPaper(currentEditQuestion, opt.key)" />
                  </label>
                  <button class="ep-opt-del" @click="currentEditQuestion.options.splice(oIdx, 1)"
                          :disabled="currentEditQuestion.options.length <= 2" title="删除选项">�?/button>
                </div>
                <button class="ep-add-opt-btn" @click="addOptionToQuestion(currentEditQuestion)">+ 添加选项</button>
              </div>
            </div>

            <!-- 判断�?-->
            <div v-else-if="currentEditQuestion.type === 'true_false'" class="ep-field">
              <label>正确答案</label>
              <div class="ep-tf-group">
                <label :class="['ep-tf-btn', { active: currentEditQuestion.answer === 'true' }]">
                  <input type="radio" :name="'etf_' + selectedEditIndex" value="true" v-model="currentEditQuestion.answer" />
                  �?正确
                </label>
                <label :class="['ep-tf-btn', { active: currentEditQuestion.answer === 'false' }]">
                  <input type="radio" :name="'etf_' + selectedEditIndex" value="false" v-model="currentEditQuestion.answer" />
                  �?错误
                </label>
              </div>
            </div>

            <!-- 填空/简�?-->
            <div v-else class="ep-field">
              <label>参考答�?/label>
              <textarea v-model="currentEditQuestion.answer" rows="3" class="form-input ep-answer-area" placeholder="输入参考答�?.."></textarea>
            </div>
          </div>
        </div>

        <div v-else class="ep-editor ep-empty-hint">
          <div class="empty-icon">📝</div>
          <p>从左侧选择一道题目开始编�?/p>
        </div>
      </div>

      <template #footer>
        <div class="ep-modal-footer">
          <div class="ep-order-btns">
            <button class="btn btn-outline btn-sm" :disabled="selectedEditIndex <= 0" @click="moveQuestionUp(selectedEditIndex); if(selectedEditIndex > 0) selectedEditIndex--">�?上移</button>
            <button class="btn btn-outline btn-sm" :disabled="selectedEditIndex >= editingPaper.questionList.length - 1" @click="moveQuestionDown(selectedEditIndex); if(selectedEditIndex < editingPaper.questionList.length - 1) selectedEditIndex++">�?下移</button>
            <button class="btn btn-danger btn-sm" @click="handleDeleteSelectedQuestion">🗑 删除此题</button>
          </div>
          <div class="ep-action-btns">
            <a-button @click="handleCancelEditPaper">取消</a-button>
            <a-button type="primary" @click="handleSaveEditedPaper" :disabled="!editingPaper?.questionList?.length">保存修改</a-button>
          </div>
        </div>
      </template>
    </a-modal>

    <a-modal
      title="编辑题目"
      v-model:open="showEditQuestionModal"
      @ok="handleSaveQuestion"
      @cancel="showEditQuestionModal = false"
      width="650px"
    >
      <a-form layout="vertical">
        <a-form-item label="题目内容">
          <a-textarea v-model:value="editQuestion.stem" :rows="4" placeholder="请输入题目内�? />
        </a-form-item>
        <a-form-item label="题目类型">
          <select v-model="editQuestion.type" class="form-select" @change="onEditTypeChange">
            <option value="single">单选题</option>
            <option value="multiple">多选题</option>
            <option value="judgment">判断�?/option>
            <option value="essay">简答题</option>
          </select>
        </a-form-item>

        <!-- 单选题选项 -->
        <a-form-item label="选项设置" v-if="editQuestion.type === 'single'">
          <div class="edit-options-list">
            <div v-for="(opt, idx) in editQuestion.options" :key="idx" class="edit-option-row" :class="{ 'option-selected': editQuestion.answer === ['A','B','C','D','E','F'][idx] }">
              <span class="option-key">{{ ['A','B','C','D','E','F'][idx] }}.</span>
              <a-input v-model:value="opt.content" class="option-input" placeholder="选项内容" />
              <a-radio-group v-model:value="editQuestion.answer" class="option-radio-group">
                <a-radio :value="['A','B','C','D','E','F'][idx]">正确</a-radio>
              </a-radio-group>
              <button class="btn-remove-option" @click="removeEditOption(idx)" v-if="editQuestion.options.length > 2">�?/button>
            </div>
            <button class="btn-add-option" @click="addEditOption" v-if="editQuestion.options.length < 6">+ 添加选项</button>
          </div>
        </a-form-item>

        <!-- 多选题选项 -->
        <a-form-item label="选项设置" v-if="editQuestion.type === 'multiple'">
          <div class="edit-options-list">
            <div v-for="(opt, idx) in editQuestion.options" :key="idx" class="edit-option-row" :class="{ 'option-selected': isMultiCorrect(idx) }">
              <span class="option-key">{{ ['A','B','C','D','E','F'][idx] }}.</span>
              <a-input v-model:value="opt.content" class="option-input" placeholder="选项内容" />
              <a-checkbox :checked="isMultiCorrect(idx)" @change="toggleEditMultiAnswer(['A','B','C','D','E','F'][idx])" class="option-check">正确</a-checkbox>
              <button class="btn-remove-option" @click="removeEditOption(idx)" v-if="editQuestion.options.length > 2">�?/button>
            </div>
            <button class="btn-add-option" @click="addEditOption" v-if="editQuestion.options.length < 8">+ 添加选项</button>
          </div>
          <div class="edit-multiple-hint">可勾选多个正确答�?/div>
        </a-form-item>

        <!-- 判断�?-->
        <a-form-item label="正确答案" v-if="editQuestion.type === 'judgment'">
          <a-radio-group v-model:value="editQuestion.answer" class="edit-judgment-group">
            <a-radio value="正确" class="edit-judgment-radio edit-judgment-true">�?正确</a-radio>
            <a-radio value="错误" class="edit-judgment-radio edit-judgment-false">�?错误</a-radio>
          </a-radio-group>
        </a-form-item>

        <!-- 简答题 -->
        <a-form-item label="参考答�? v-if="editQuestion.type === 'essay'">
          <a-textarea v-model:value="editQuestion.answer" :rows="5" placeholder="请输入参考答�? />
        </a-form-item>

        <!-- 解析 -->
        <a-form-item label="题目解析">
          <a-textarea v-model:value="editQuestion.explanation" :rows="3" placeholder="请输入题目解析（选填�? />
        </a-form-item>

        <a-form-item label="难度等级">
          <a-radio-group v-model:value="editQuestion.difficulty" class="edit-difficulty-group">
            <a-radio value="easy" class="edit-difficulty-radio edit-diff-easy">🟢 简�?/a-radio>
            <a-radio value="medium" class="edit-difficulty-radio edit-diff-medium">🟡 中等</a-radio>
            <a-radio value="hard" class="edit-difficulty-radio edit-diff-hard">🔴 困难</a-radio>
          </a-radio-group>
        </a-form-item>
      </a-form>
    </a-modal>

    <a-modal
      title="批量导入题目"
      v-model:open="showBatchImportModal"
      :footer="null"
      :closable="false"
    >
      <div style="text-align: center; padding: 40px 0;">
        <div style="font-size: 48px; margin-bottom: 16px;">📁</div>
        <h3 style="margin-bottom: 16px;">正在导入题目...</h3>
        <p style="color: #999;">请稍候，正在解析题目文件</p>
      </div>
    </a-modal>

    <a-modal
      title="正在生成试卷"
      v-model:open="showGeneratePaperModal"
      :footer="null"
      :closable="false"
    >
      <div style="text-align: center; padding: 40px 0;">
        <div style="font-size: 48px; margin-bottom: 16px;">📝</div>
        <h3 style="margin-bottom: 16px;">正在智能组卷...</h3>
        <!-- 等待动画（替代原来的百分比进度条�?-->
        <div class="waiting-indicator" style="margin-top: 24px;">
          <div class="waiting-dots">
            <span></span><span></span><span></span>
          </div>
        </div>
        <p style="color: #666; margin-top: 20px; font-size: 14px;">
          请稍候，AI 正在为您生成试卷...
        </p>
      </div>
    </a-modal>

    <a-modal
      title="查看试卷"
      v-model:open="showViewPaperModal"
      @cancel="showViewPaperModal = false"
      width="800px"
      :footer="null"
      :destroyOnClose="true"
      :maskClosable="true"
    >
      <div v-if="previewPaper">
        <a-descriptions bordered :column="1" size="middle">
          <a-descriptions-item label="试卷名称">
            {{ previewPaper.name }}
          </a-descriptions-item>
          <a-descriptions-item label="关联制度">
            {{ previewPaper.doc || '-' }}
          </a-descriptions-item>
          <a-descriptions-item label="题目数量">
            {{ previewPaper.questions }} �?          </a-descriptions-item>
          <a-descriptions-item label="总分">
            {{ previewPaper.totalScore }} �?          </a-descriptions-item>
          <a-descriptions-item label="考试时长">
            {{ previewPaper.duration }} 分钟
          </a-descriptions-item>
          <a-descriptions-item label="试卷状�?>
            <a-badge :status="previewPaper.status === 'published' ? 'success' : 'warning'" :text="previewPaper.status === 'published' ? '已发�? : '草稿'" />
          </a-descriptions-item>
        </a-descriptions>

        <div style="margin-top: 24px;">
          <!-- 预览工具�?-->
          <div class="preview-toolbar">
            <h4 class="preview-title">📋 试卷题目预览</h4>
            <div class="preview-actions">
              <span class="preview-stats">
                �?<strong>{{ previewPaper.questionList?.length || 0 }}</strong> �?                <span v-if="shouldEnablePagination">
                  （第 {{ previewPagination.current }}/{{ previewTotalPages }} 页）
                </span>
              </span>
              <button 
                v-if="previewPaper.questionList?.length > 0"
                class="btn btn-sm" 
                :class="isAllExpanded ? 'btn-outline' : 'btn-primary'"
                @click="isAllExpanded ? collapseAllQuestions() : expandAllQuestions()"
              >
                {{ isAllExpanded ? '📂 全部收起' : '📂 全部展开' }}
              </button>
            </div>
          </div>

          <!-- 题目列表 -->
          <div v-if="previewPaper.questionList && previewPaper.questionList.length > 0" class="preview-questions-container">

            <!-- 使用分页后的题目列表 -->
            <div v-for="(q, idx) in (shouldEnablePagination ? previewCurrentPageQuestions : previewPaper.questionList)" 
                 :key="idx"
                 :class="['preview-question-item', { 'expanded': isQuestionExpanded(idx) }]">
              
              <!-- 题目头部（始终显示）-->
              <div class="preview-q-header" @click="toggleQuestionExpand(idx)">
                <div class="preview-q-info">
                  <span :class="['type-badge', q.type]">{{ q.typeLabel || formatQuestionType(q.type) }}</span>
                  <strong>�?{{ shouldEnablePagination ? (previewPagination.current - 1) * previewPagination.pageSize + idx + 1 : idx + 1 }} �?/strong>
                  <span class="preview-q-score">（{{ q.score || 5 }} 分）</span>
                </div>
                <span class="preview-toggle-icon">{{ isQuestionExpanded(idx) ? '�? : '�? }}</span>
              </div>

              <!-- 折叠模式：仅显示题干摘要 -->
              <div v-if="!isQuestionExpanded(idx)" class="preview-q-summary">
                <p class="preview-stem-truncated">{{ truncateStem(q.stem) }}</p>
                <span class="preview-hint-text">点击展开查看完整内容和答�?/span>
              </div>

              <!-- 展开模式：显示完整内�?-->
              <div v-if="isQuestionExpanded(idx)" class="preview-q-full">
                <div class="preview-q-stem">{{ q.stem }}</div>

                <!-- 单选题/多选题选项 -->
                <div v-if="q.options && q.options.length > 0 && isChoiceType(q.type)" class="preview-options-list">
                  <div v-for="opt in q.options" :key="opt.key"
                       :class="['preview-option-item', { 'correct-answer': isPreviewCorrectAnswer(q, opt.key) }]">
                    <span class="preview-opt-key">{{ opt.key }}</span>
                    <span class="preview-opt-content">{{ opt.content }}</span>
                    <span v-if="isPreviewCorrectAnswer(q, opt.key)" class="correct-badge">�?正确答案</span>
                  </div>
                </div>

                <!-- 判断�?-->
                <div v-else-if="q.type === 'true_false' || q.type === 'judgment'" class="preview-options-list preview-inline">
                  <div :class="['preview-option-item', { 'correct-answer': isTrueAnswer(q.answer) }]">
                    <span>�?正确</span>
                    <span v-if="isTrueAnswer(q.answer)" class="correct-badge">�?正确答案</span>
                  </div>
                  <div :class="['preview-option-item', { 'correct-answer': isFalseAnswer(q.answer) }]">
                    <span>�?错误</span>
                    <span v-if="isFalseAnswer(q.answer)" class="correct-badge">�?正确答案</span>
                  </div>
                </div>

                <!-- 填空/简答题 -->
                <div v-else class="preview-answer-section">
                  <label class="preview-answer-label">参考答案：</label>
                  <div class="preview-answer-content">{{ formatAnswer(q.answer, q.type) }}</div>
                </div>
              </div>
            </div>
          </div>

          <!-- 空状�?-->
          <div v-else style="text-align: center; padding: 40px; color: #999;">
            暂无题目预览
          </div>

          <!-- 分页控件（仅题目数量>10时显示） -->
          <div v-if="shouldEnablePagination && previewTotalPages > 1" class="preview-pagination-wrapper">
            <div class="preview-pagination">
              <button 
                class="btn btn-page-nav"
                :disabled="previewPagination.current <= 1"
                @click="previewPrevPage()">
                « 上一�?              </button>

              <div class="preview-pages">
                <button 
                  v-for="page in getVisiblePageNumbers(previewPagination.current, previewTotalPages)"
                  :key="page"
                  :class="['btn btn-page-num', { active: page === previewPagination.current, ellipsis: page === '...' }]"
                  :disabled="page === '...'"
                  @click="page !== '...' && goToPreviewPage(page)">
                  {{ page }}
                </button>
              </div>

              <button 
                class="btn btn-page-nav"
                :disabled="previewPagination.current >= previewTotalPages"
                @click="previewNextPage()">
                下一�?»
              </button>
            </div>

            <div class="preview-page-info">
              显示�?<strong>{{ (previewPagination.current - 1) * previewPagination.pageSize + 1 }}</strong> - 
              <strong>{{ Math.min(previewPagination.current * previewPagination.pageSize, currentPaper.questionList.length) }}</strong> 题，
              �?<strong>{{ currentPaper.questionList.length }}</strong> �?            </div>
          </div>
        </div>
      </div>

      <template #footer>
        <a-button @click="showViewPaperModal = false">关闭</a-button>
        <a-button 
          v-if="previewPaper && previewPaper.status !== 'published'" 
          type="primary" 
          @click="handleOpenPublishModalFromView(previewPaper)">
          📢 发布试卷
        </a-button>
        <a-badge 
          v-else-if="previewPaper && previewPaper.status === 'published'" 
          status="success" 
          text="已发�? 
          style="margin-left: 12px;" />
      </template>
    </a-modal>

    <a-modal
      title="闯关挑战"
      v-model:open="showCheckpointModal"
      @cancel="showCheckpointModal = false"
      width="700px"
    >
      <template #footer>
        <a-button @click="showCheckpointModal = false">退�?/a-button>
        <a-button type="primary" @click="handleSubmitCheckpoint" :loading="isGrading" :disabled="!allCheckpointAnswered">
          {{ isGrading ? '批改�?..' : '提交答案' }}
        </a-button>
      </template>
      <div v-if="currentCheckpoint && currentCheckpointQuestions.length > 0">
        <h3 style="margin-bottom: 16px; color: #1890ff;">{{ currentCheckpoint.name }}</h3>

        <div v-for="(q, index) in currentCheckpointQuestions" :key="q.id" style="margin-bottom: 20px; padding: 16px; background: #fafafa; border-radius: 8px; border: 1px solid #e8e8e8;">
          <div style="margin-bottom: 8px;">
            <span class="type-badge">{{ q.typeLabel }}</span>
            <span style="margin-left: 8px;">�?{{ index + 1 }} 题（{{ q.score }} 分）</span>
          </div>

          <div style="margin-bottom: 12px;">{{ q.stem }}</div>

          <div v-if="q.type === 'single_choice' && q.options?.length">
            <label v-for="opt in q.options" :key="opt.key" style="display: block; margin-bottom: 6px; cursor: pointer;">
              <input type="radio" :name="'cp_' + q.id" :value="opt.key" v-model="q.studentAnswer" />
              <span style="margin-left: 4px;">{{ opt.key }}. {{ opt.content }}</span>
            </label>
          </div>

          <div v-if="q.type === 'multiple_choice' && q.options?.length">
            <label v-for="opt in q.options" :key="opt.key" style="display: block; margin-bottom: 6px; cursor: pointer;">
              <input type="checkbox" :value="opt.key" @change="toggleMultiCheckpointAnswer(q, opt.key)" :checked="isMultiCheckpointSelected(q, opt.key)" />
              <span style="margin-left: 4px;">{{ opt.key }}. {{ opt.content }}</span>
            </label>
          </div>

          <div v-if="q.type === 'true_false'">
            <label style="display: inline-block; margin-right: 16px; cursor: pointer;">
              <input type="radio" :name="'cp_' + q.id" value="true" v-model="q.studentAnswer" />
              <span style="margin-left: 4px;">正确</span>
            </label>
            <label style="display: inline-block; cursor: pointer;">
              <input type="radio" :name="'cp_' + q.id" value="false" v-model="q.studentAnswer" />
              <span style="margin-left: 4px;">错误</span>
            </label>
          </div>

          <div v-if="q.type === 'subjective'">
            <textarea v-model="q.studentAnswer" rows="3" style="width: 100%; padding: 8px; border: 1px solid #d9d9d9; border-radius: 4px;" placeholder="请输入您的答�?.."></textarea>
          </div>
        </div>
      </div>
      <div v-else style="text-align: center; padding: 40px; color: #999;">
        正在加载题目...
      </div>
    </a-modal>

    <a-modal
      title="答题结果"
      v-model:open="showAnswerModal"
      @cancel="showAnswerModal = false"
      width="600px"
      :footer="null"
    >
      <div class="grade-result-content">
        <div class="grade-result-header">
          <div class="grade-score-circle" :class="scoreLevelClass">
            <span class="score-value">{{ gradeResult?.total_score || 0 }}</span>
            <span class="score-divider">/</span>
            <span class="score-max">{{ gradeResult?.total_max_score || 0 }}</span>
          </div>
          <div class="grade-rate">{{ gradeResult?.score_rate || 0 }}%</div>
        </div>

        <div class="grade-results-list" v-if="gradeResult?.results?.length">
          <div 
            v-for="(item, idx) in gradeResult.results" 
            :key="idx" 
            :class="['grade-result-item', { correct: item.correct, wrong: item.correct === false }]"
          >
            <div class="grade-item-header">
              <span class="grade-item-num">�?{{ idx + 1 }} �?/span>
              <span class="grade-item-score">
                {{ item.score }}/{{ item.max_score }} �?              </span>
              <span v-if="item.correct === true" class="grade-item-tag correct">�?正确</span>
              <span v-else-if="item.correct === false" class="grade-item-tag wrong">�?错误</span>
              <span v-else class="grade-item-tag subjective">主观�?/span>
            </div>
            <div v-if="item.feedback" class="grade-item-feedback">{{ item.feedback }}</div>
            <div v-if="item.details?.overall_feedback" class="grade-item-ai-feedback">
              <strong>AI评语�?/strong>{{ item.details.overall_feedback }}
            </div>
          </div>
        </div>

        <div v-if="!gradeResult?.results?.length && !isGrading" class="grade-empty">
          <p>暂无批改结果</p>
        </div>

        <button class="btn btn-primary" style="width: 100%; margin-top: 20px;" @click="showAnswerModal = false">
          关闭
        </button>
      </div>
    </a-modal>

    <a-modal
      title="查看解析"
      v-model:open="showAnalysisModal"
      @cancel="showAnalysisModal = false"
      width="700px"
    >
      <template #footer>
        <a-button @click="showAnalysisModal = false">关闭</a-button>
      </template>
      <div v-if="currentWrongQuestion" class="analysis-content">
        <div class="analysis-type-tag">
          <span class="type-badge" :class="currentWrongQuestion.type">{{ currentWrongQuestion.typeLabel || formatQuestionType(currentWrongQuestion.type) }}</span>
          <span class="analysis-score">得分: {{ currentWrongQuestion.score || 0 }} / {{ currentWrongQuestion.maxScore || 5 }} �?/span>
        </div>

        <h4 style="margin-bottom: 16px;">{{ currentWrongQuestion.content }}</h4>

        <div class="analysis-section">
          <h6>📝 你的答案�?/h6>
          <p class="student-answer">{{ currentWrongQuestion.studentAnswer || '未作�? }}</p>
        </div>

        <div class="analysis-section">
          <h6>�?标准答案�?/h6>
          <p class="correct-answer">{{ formatAnswer(currentWrongQuestion.correctAnswer) }}</p>
        </div>

        <div v-if="currentWrongQuestion.feedback" class="analysis-section">
          <h6>💡 批改反馈�?/h6>
          <p>{{ currentWrongQuestion.feedback }}</p>
        </div>

        <div v-if="currentWrongQuestion.options && currentWrongQuestion.options.length > 0" class="analysis-section">
          <h6>📋 选项�?/h6>
          <div v-for="opt in currentWrongQuestion.options" :key="opt.key" class="analysis-option" :class="{ 'option-correct': opt.key === currentWrongQuestion.correctAnswer }">
            <strong>{{ opt.key }}.</strong> {{ opt.content }}
            <span v-if="opt.key === currentWrongQuestion.correctAnswer" class="correct-mark">�?正确答案</span>
          </div>
        </div>

        <div style="display: flex; justify-content: flex-end; gap: 8px; margin-top: 16px;">
          <a-button @click="handleRetryWrongQuestion(currentWrongQuestion)">重新练习</a-button>
        </div>
      </div>
    </a-modal>
  </div>
</template>

<script setup>
import { ref, reactive, computed, watch, onMounted, nextTick } from 'vue'
import { message, Modal } from 'ant-design-vue'
import {
  QuestionCircleOutlined,
  BookOutlined,
  FileTextOutlined,
  CheckCircleOutlined,
  PlayCircleOutlined,
  EditOutlined,
  DeleteOutlined,
  UploadOutlined,
  EyeOutlined
} from '@ant-design/icons-vue'
import { hasChildPermission } from '../utils/permission.js'
import FileSelector from './FileSelector.vue'
import { fileAPI } from '../api/file.js'
import { questionAPI } from '../api/question.js'
import { examAPI } from '../api/exam.js'
import { departmentAPI, userAPI } from '../api/auth.js'

const activeTab = ref('generate')
const trainTab = ref('checkpoint')

// ========== 试卷考核状�?==========
const examTab = ref('pending')
const pendingPapers = ref([])
const currentTakingPaper = ref(null)
const currentQuestionIndex = ref(0)
const takingAnswers = ref({})
const isSubmitting = ref(false)
const completedResults = ref([])

const loadPendingPapers = async () => {
  isLoadingPapers.value = true
  try {
    let result = await examAPI.getMyPapers()
    if (!result || (Array.isArray(result) && result.length === 0)) {
      console.log('[试卷考核] 正式考列表为空，尝试模拟考接�?..')
      try { result = await examClient.get('/exam/my/papers', { timeout: 10000 }).then(r => r.data?.data || r.data) } catch {}
    }
    pendingPapers.value = Array.isArray(result) ? result : (result.papers || result.list || [])
    console.log('[试卷考核] 待考试列表加载完成:', pendingPapers.value.length, '�?)
  } catch (error) {
    console.error('[试卷考核] 加载待考试列表失败:', error)
    pendingPapers.value = []
  } finally {
    isLoadingPapers.value = false
  }
}

// 监听切换到试卷考核tab时，自动刷新待考试列表
watch(activeTab, (newVal) => {
  if (newVal === 'exam') {
    // 每次切换到试卷考核tab都刷�?    loadPendingPapers()
  }
})

// 监听切换到待考试子tab时也刷新
watch(examTab, (newVal) => {
  if (newVal === 'pending') {
    loadPendingPapers()
  }
}, { immediate: true })
const examWrongQuestions = ref([])
const isLoadingPapers = ref(false)
const isLoadingWrongQuestions = ref(false)
const isGeneratingSelftest = ref(false)
const currentRecordId = ref(null)

const selftestConfig = reactive({
  singleCount: 5,
  multipleCount: 3,
  judgmentCount: 2,
  essayCount: 1,
  difficulty: 3
})

const currentQuestion = computed(() => {
  if (!currentTakingPaper.value?.questionList) return null
  return currentTakingPaper.value.questionList[currentQuestionIndex.value] || null
})

const totalQuestions = computed(() => {
  return currentTakingPaper.value?.questionList?.length || 0
})

const answeredCount = computed(() => {
  if (!currentTakingPaper.value?.questionList) return 0
  return currentTakingPaper.value.questionList.filter(q => !!takingAnswers.value[q.questionId]).length
})
const ragServiceAvailable = ref(null)
const ragServiceChecking = ref(false)

// 检查出题服务是否可用（简化版：不依赖健康检查接口，直接允许使用�?const checkRagService = async () => {
  // 不再调用 healthCheck 接口（该接口不存在）
  // 直接返回 true，允许用户使用AI出题功能
  // 如果后端服务不可用，会在实际生成题目时返回错误信�?  ragServiceAvailable.value = true
  return true
}

// 待审核列表的状态筛�?const pendingListStatus = ref('pending')

// 分页查询生成的题�?const loadGeneratedQuestions = async (page = 1) => {
  if (questionPagination.loading) return

  questionPagination.loading = true
  questionPagination.current = page

  try {
    const params = {
      pageNum: page,
      pageSize: questionPagination.pageSize
    }
    if (pendingListStatus.value && pendingListStatus.value !== 'all') {
      params.status = pendingListStatus.value
    }

    const res = await questionAPI.pageQuestions(params)

    console.log('📚 分页查询题目响应:', res)

    let questionsList = []
    let total = 0

    if (res?.data?.code === 200 && res.data.data) {
      if (res.data.data.records) {
        questionsList = res.data.data.records
        total = res.data.data.total || questionsList.length
      } else if (Array.isArray(res.data.data)) {
        questionsList = res.data.data
        total = questionsList.length
      } else if (res.data.data.list) {
        questionsList = res.data.data.list
        total = res.data.data.total || questionsList.length
      }
    }

    const typeLabelMap = {
      single_choice: '单选题',
      multiple_choice: '多选题',
      true_false: '判断�?,
      fill_blank: '填空�?,
      subjective: '简答题'
    }

    const typeClassMap = {
      single_choice: 'single',
      multiple_choice: 'multiple',
      true_false: 'judgment',
      fill_blank: 'essay',
      subjective: 'essay'
    }

    generatedQuestions.value = questionsList.map((q, index) => {
      const qType = q.questionType || q.question_type || q.type || 'single_choice'
      const content = q.content || {}
      const stem = content?.stem || q.stem || q.questionText || ''
      const options = content?.data?.options || content?.options || q.options || []
      const answer = content?.answer || q.answer || ''
      const explanation = content?.explanation || q.explanation || ''

      return {
        id: q.id,
        questionId: q.questionId,
        type: qType,
        typeLabel: typeLabelMap[qType] || qType,
        typeClass: typeClassMap[qType] || 'single',
        difficulty: q.difficulty || difficulty.value,
        stem: stem,
        options: options,
        answer: answer,
        explanation: explanation,
        sourceTrace: q.sourceTrace || {},
        status: q.status || 'pending',
        studentAnswer: null,
        maxScore: qType === 'subjective' ? 20 : (qType === 'multiple_choice' ? 10 : (qType === 'fill_blank' ? 8 : 5)),
        _raw: q
      }
    })

    questionPagination.total = total

    console.log(`📚 �?{page}页加载完�? ${questionsList.length}条记�? 总计${total}条`)
  } catch (error) {
    console.error('�?分页查询题目失败:', error)
    message.error('加载题目列表失败，请重试')
  } finally {
    questionPagination.loading = false
  }
}

// 计算总页�?const totalPages = computed(() => {
  return Math.ceil(questionPagination.total / questionPagination.pageSize) || 1
})

// 获取可见的页码列表（显示当前页附近的页码�?const getVisiblePages = (current, total) => {
  current = current || 1
  total = total || 1
  const pages = []

  if (total <= 7) {
    for (let i = 1; i <= total; i++) {
      pages.push(i)
    }
  } else {
    if (current <= 4) {
      for (let i = 1; i <= 5; i++) pages.push(i)
      pages.push('...')
      pages.push(total)
    } else if (current >= total - 3) {
      pages.push(1)
      pages.push('...')
      for (let i = total - 4; i <= total; i++) pages.push(i)
    } else {
      pages.push(1)
      pages.push('...')
      for (let i = current - 1; i <= current + 1; i++) pages.push(i)
      pages.push('...')
      pages.push(total)
    }
  }

  return pages
}

// 判断选项是否为正确答�?const isOptionCorrect = (q, optKey) => {
  if (!q.answer) return false
  const answerStr = Array.isArray(q.answer) ? q.answer.join(',') : String(q.answer)
  return answerStr.includes(optKey)
}

// 待审核列�?- 跳转页码
const jumpPagePending = ref(null)
const handleJumpPagePending = () => {
  const page = parseInt(jumpPagePending.value)
  if (!page || page < 1 || page > totalPages.value) {
    message.warning(`请输�?1~${totalPages.value} 之间的页码`)
    return
  }
  loadGeneratedQuestions(page)
  jumpPagePending.value = null
}

// 刷新待审核题目列�?const refreshQuestionList = async () => {
  console.log('🔄 手动刷新待审核题目列�?..')
  await loadGeneratedQuestions(1)
  if (questionPagination.total > 0) {
    message.success(`�?刷新成功！共 ${questionPagination.total} 道待审核题目`)
  } else {
    message.info('当前暂无待审核题�?)
  }
}

// 采纳题目
const handleApproveQuestion = async (q) => {
  const reviewId = q.questionId || q.id
  if (!reviewId) {
    message.error('题目ID缺失，无法操�?)
    console.error('�?题目缺少 questionId �?id:', q)
    return
  }

  try {
    const result = await questionAPI.reviewQuestion(reviewId, 'approved', '管理员采�?)
    const resData = result?.data
    if (resData?.code === 200 || resData?.success === true) {
      console.log('�?题目已采�?', reviewId, resData)
      message.success(`�?题目已采纳！`)
      await refreshQuestionList()
    } else {
      console.warn('⚠️ 采纳响应异常:', resData)
      message.error(resData?.message || '采纳操作未成功，请重�?)
    }
  } catch (error) {
    console.error('�?采纳题目失败:', error)
    message.error('操作失败，请重试')
  }
}

// 拒绝题目
const handleRejectQuestion = async (q) => {
  const reviewId = q.questionId || q.id
  if (!reviewId) {
    message.error('题目ID缺失，无法操�?)
    console.error('�?题目缺少 questionId �?id:', q)
    return
  }

  try {
    const result = await questionAPI.reviewQuestion(reviewId, 'rejected', '管理员拒�?)
    const resData = result?.data
    if (resData?.code === 200 || resData?.success === true) {
      console.log('�?题目已拒�?', reviewId, resData)
      message.warning(`题目已拒绝`)
      await refreshQuestionList()
    } else {
      console.warn('⚠️ 拒绝响应异常:', resData)
      message.error(resData?.message || '拒绝操作未成功，请重�?)
    }
  } catch (error) {
    console.error('�?拒绝题目失败:', error)
    message.error('操作失败，请重试')
  }
}

const normalizeDifficulty = (val) => {
  if (!val) return 'easy'
  if (typeof val === 'string') return val
  if (typeof val === 'number') {
    if (val <= 2) return 'easy'
    if (val <= 4) return 'medium'
    return 'hard'
  }
  return 'easy'
}

const loadQuestionsFromDB = async () => {
  try {
    const params = { pageNum: 1, pageSize: 200 }
    if (bankFilter.status && bankFilter.status !== 'all') {
      params.status = bankFilter.status
    }
    if (bankFilter.type && bankFilter.type !== 'all') {
      const typeReverseMap = {
        single: 'single_choice',
        multiple: 'multiple_choice',
        judgment: 'true_false',
        essay: 'fill_blank'
      }
      const mappedType = typeReverseMap[bankFilter.type]
      if (mappedType) {
        params.questionType = mappedType
      }
    }

    const res = await questionAPI.pageQuestions(params)
    if (res.data?.code === 200 && res.data?.data?.records) {
        const records = res.data.data.records
        console.log('📚 后端返回的所有字�?', Object.keys(records[0] || {}))
        console.log('📚 后端返回的第一条记�?', JSON.stringify(records[0], null, 2))
        console.log('📚 后端所有题型原始�?', records.map(r => r.questionType || r.question_type || r.type))
        const typeMap = {
        single_choice: 'single',
        multiple_choice: 'multiple',
        true_false: 'judgment',
        fill_blank: 'essay',
        subjective: 'essay'
      }
      questions.value = records.map((r, i) => {
        let stem = ''
        let answer = ''
        let explanation = ''
        let options = []
        
        const rawContent = r.content || r.questionContent || ''
        console.log('📄 r.content 类型:', typeof rawContent)
        console.log('📄 r.stem:', r.stem)
        console.log('📄 r.answer:', r.answer)
        
        if (r.stem) {
          stem = r.stem
          answer = r.answer || ''
          explanation = r.explanation || ''
          options = r.data?.options || r.options || []
        } else if (typeof rawContent === 'string' && rawContent.trim().startsWith('{')) {
          try {
            const parsed = JSON.parse(rawContent)
            stem = parsed.stem || rawContent
            answer = parsed.answer || ''
            explanation = parsed.explanation || ''
            options = parsed.data?.options || []
          } catch (e) {
            stem = rawContent
          }
        } else if (typeof rawContent === 'object' && rawContent !== null) {
          stem = rawContent.stem || ''
          answer = rawContent.answer || ''
          explanation = rawContent.explanation || ''
          options = rawContent.data?.options || rawContent.options || []
        } else {
          stem = rawContent
        }
        
        return {
          id: r.id || i + 1,
          content: rawContent,
          stem: stem,
          answer: answer,
          explanation: explanation,
          options: options,
          type: typeMap[r.questionType] || typeMap[r.question_type] || 'single',
          difficulty: normalizeDifficulty(r.difficulty),
          doc: r.documentName || r.document_name || '',
          status: r.status || 'pending',
          expanded: false
        }
      })
      console.log('📚 处理后的第一�?', JSON.stringify(questions.value[0], null, 2))
      console.log('📚 所有题型分�?', questions.value.reduce((acc, q) => {
        acc[q.type] = (acc[q.type] || 0) + 1
        return acc
      }, {}))
    }
  } catch (e) {
    console.warn('从题库加载题目失�?', e.message)
  }
}

// 权限控制 - 严格校验，每个子功能独立控制
const canShowGenerate = computed(() => hasChildPermission('exam:ai'))
const canShowBank = computed(() => hasChildPermission('exam:bank'))
const canShowPaper = computed(() => hasChildPermission('exam:paper'))
const canShowTrain = computed(() => hasChildPermission('exam:train'))
const canShowExam = computed(() => hasChildPermission('exam:exam'))

// 获取第一个有权限的标签页
const getFirstAccessibleTab = () => {
  if (canShowGenerate.value) return 'generate'
  if (canShowBank.value) return 'bank'
  if (canShowPaper.value) return 'paper'
  if (canShowExam.value) return 'exam'
  if (canShowTrain.value) return 'train'
  return null
}

// 监听权限变化，确保当前标签页有访问权�?watch(
  [canShowGenerate, canShowBank, canShowPaper, canShowExam, canShowTrain],
  () => {
    const hasAccess =
      (activeTab.value === 'generate' && canShowGenerate.value) ||
      (activeTab.value === 'bank' && canShowBank.value) ||
      (activeTab.value === 'paper' && canShowPaper.value) ||
      (activeTab.value === 'exam' && canShowExam.value) ||
      (activeTab.value === 'train' && canShowTrain.value)
    
    if (!hasAccess) {
      // 切换到第一个有权限的标签页
      const firstTab = getFirstAccessibleTab()
      if (firstTab) {
        activeTab.value = firstTab
      }
    }
  },
  { immediate: true }
)

// 组件挂载时确保标签页有访问权�?onMounted(async () => {
  const firstTab = getFirstAccessibleTab()
  if (firstTab) {
    activeTab.value = firstTab
  }

  checkRagService()

  await loadQuestionsFromDB()

  // 自动加载待审核题目列表（常驻展示�?  console.log('📋 初始化：加载待审核题目列�?..')
  await loadGeneratedQuestions(1)

  // �?sessionStorage 恢复已生成的试卷
  const hasSaved = loadPapersFromSession()
  if (hasSaved) {
    console.log('📂 已从缓存恢复', papers.value.length, '套试�?)
  }

  // 自动加载已发布试卷列�?  console.log('📋 初始化：加载已发布试卷列�?..')
  showPublishedList.value = true
  await loadPublishedPapers()
})

const showGenerateModal = ref(false)
const showEditQuestionModal = ref(false)
const showBatchImportModal = ref(false)
const showGeneratePaperModal = ref(false)
const showViewPaperModal = ref(false)
const showEditPaperModal = ref(false)
const showCheckpointModal = ref(false)
const showAnswerModal = ref(false)
const showAnalysisModal = ref(false)
const showPublishPaperModal = ref(false)

// 发布试卷相关
const publishingPaper = ref(null)
const departmentList = ref([])
const userList = ref([])
const selectedPublishDeptId = ref(null)
const selectedPublishUserIds = ref([])
const selectedPublishExcludeUserIds = ref([])
const publishUserMode = ref('include') // 'include' = 指定用户考试�?exclude' = 排除用户考试
const userSearchKeyword = ref('')
const userPage = ref(1) // 当前页码
const userPageSize = ref(8) // 每页显示数量

// 监听搜索关键词变化，重置页码
watch(userSearchKeyword, () => {
  userPage.value = 1
})
const publishDeadline = ref('')
const publishLoading = ref(false)
const publishUserLoading = ref(false)
const publishedPapers = ref([])
const showPublishedList = ref(false)
const isLoadingPublished = ref(false)
const isViewLoading = ref(false)
const currentViewingId = ref(null)



const selectedDoc = ref(null)
const selectedDocData = ref(null)
const selectedCollectionName = ref('')  // 当前选中的向量库名称

// 向量库选择变化回调
const onCollectionChange = (collectionName) => {
  console.log('📚 选择知识�?', collectionName)
  selectedCollectionName.value = collectionName
}

const onDocSelected = (docData) => {
  selectedDocData.value = docData
}

const questionTypeOptions = [
  { label: '单选题', value: 'single_choice' },
  { label: '多选题', value: 'multiple_choice' },
  { label: '判断�?, value: 'true_false' },
  { label: '填空�?, value: 'fill_blank' },
  { label: '简答题', value: 'subjective' }
]

const questionTypeCounts = reactive({
  single_choice: 3,
  multiple_choice: 2,
  true_false: 2,
  fill_blank: 1,
  subjective: 1
})

const toggleQuestionType = (type) => {
  if (questionTypeCounts[type] > 0) {
    questionTypeCounts[type] = 0
  } else {
    questionTypeCounts[type] = type === 'single_choice' ? 3 : 2
  }
}

const totalQuestionCount = computed(() => {
  return Object.values(questionTypeCounts).reduce((sum, v) => sum + (v || 0), 0)
})

const questionTypes = ref(['single_choice', 'multiple_choice', 'true_false'])
const questionCount = ref(10)
const difficulty = ref(2)
const generateProgress = ref(0)

const currentPaper = ref(null)
const editingPaper = ref(null)

const localDraftPapers = computed(() => {
  return papers.value.filter(p => p.status !== 'published')
})

const previewPaper = computed(() => {
  const p = currentPaper.value
  if (!p) return null
  
  // 判断是否为API数据：questions是数组（题目列表）而非数字（题目数量）
  const isApiData = Array.isArray(p.questions)
  
  if (isApiData) {
    const apiQuestions = (p.questions || []).map((q, qIdx) => {
      let contentObj = {}
      let rawOptions = []
      let rawStem = ''
      
      try {
        if (typeof q.content === 'string' && q.content.trim().startsWith('{')) {
          contentObj = JSON.parse(q.content || '{}')
        } else if (typeof q.content === 'object' && q.content !== null) {
          contentObj = q.content
        }
      } catch (e) { 
        console.warn('[预览] 题目', qIdx, 'content解析失败:', e.message)
      }

      rawStem = contentObj.stem || (contentObj.data && contentObj.data.stem) || q.stem || q.question_stem || q.questionContent || q.question_content || (typeof q.content === 'string' ? q.content : '')
      
      // API数据中选项可能�?content.data.options �?content.options �?q.options
      rawOptions = contentObj.options || (contentObj.data && contentObj.data.options) || q.options || []
      if (rawOptions.length === 0 && contentObj.data && Array.isArray(contentObj.data)) {
        rawOptions = contentObj.data
      }
      
      let options = []
      if (Array.isArray(rawOptions) && rawOptions.length > 0) {
        if (rawOptions[0] && typeof rawOptions[0] === 'object' && rawOptions[0].key) {
          options = rawOptions.map(opt => ({ key: String(opt.key), content: opt.content || opt.text || '' }))
        } else {
          const optionKeys = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H']
          options = rawOptions.map((opt, idx) => ({
            key: optionKeys[idx] || String.fromCharCode(65 + idx),
            content: typeof opt === 'object' ? (opt.content || opt.text || '') : String(opt)
          }))
        }
      }

      const rawType = q.questionType || q.question_type || q.type || ''
      return {
        id: q.id || `api_q_${qIdx}`,
        questionId: q.questionId || q.question_id,
        type: String(rawType).toLowerCase(),
        typeLabel: formatQuestionType(rawType),
        difficulty: q.difficulty || 3,
        score: Number(q.score) || Number(q.questionScore) || Number(q.question_score) || 5,
        stem: rawStem,
        options: options,
        answer: q.answer !== undefined ? q.answer : (contentObj.answer || (contentObj.data && contentObj.data.answer) || ''),
        explanation: q.analysis || q.explanation || contentObj.analysis || (contentObj.data && contentObj.data.analysis) || ''
      }
    })
    
    console.log('[预览] API数据转换完成, 题目�?', apiQuestions.length)
    apiQuestions.forEach((q, i) => {
      console.log(`[预览] �?{i+1}:`, JSON.stringify({ type: q.type, optionsCount: q.options?.length, answer: q.answer }))
    })
    
    return {
      name: p.title || p.name || '未命名试�?,
      doc: p.permissionScope || p.deptName || '',
      questions: p.questionCount || apiQuestions.length,
      totalScore: Number(p.totalScore) || 100,
      duration: p.duration || 60,
      status: p.status || 'published',
      questionList: apiQuestions
    }
  }
  
  console.log('[预览] 使用本地数据, questionList长度:', p.questionList?.length)
  return p
})

const formatScore = (val) => {
  if (val == null) return '?'
  return Number(val)
}
const editingSnapshot = ref(null)
const selectedEditIndex = ref(0)
const currentCheckpoint = ref(null)
const currentWrongQuestion = ref(null)

// ========== 查看试卷预览 - 分页与折叠状�?==========
const previewPagination = reactive({
  current: 1,
  pageSize: 5,              // 每页显示5道题
  total: 0
})
const expandedQuestions = ref(new Set())  // 已展开的题目ID集合
const isAllExpanded = ref(false)          // 是否全部展开

// 计算属性：当前页的题目列表
const previewCurrentPageQuestions = computed(() => {
  if (!previewPaper.value?.questionList) return []

  const start = (previewPagination.current - 1) * previewPagination.pageSize
  const end = start + previewPagination.pageSize
  return previewPaper.value.questionList.slice(start, end)
})

// 计算属性：总页�?const previewTotalPages = computed(() => {
  if (!previewPaper.value?.questionList) return 0
  return Math.ceil(previewPaper.value.questionList.length / previewPagination.pageSize) || 1
})

// 计算属性：是否启用分页（超�?0题时自动启用�?const shouldEnablePagination = computed(() => {
  return previewPaper.value?.questionList?.length > 10
})

const generatedQuestions = ref([])

// 分页状�?const questionPagination = reactive({
  current: 1,
  pageSize: 10,
  total: 0,
  loading: false
})

const questionTypeHistory = ref({})

const editQuestion = reactive({
  id: null,
  stem: '',
  type: 'single',
  answer: '',
  explanation: '',
  options: [],
  difficulty: 'easy'
})

const previousType = ref('single')

const onEditTypeChange = () => {
  // 保存旧题型状态（在v-model更新type之前记录的状态）
  const oldType = previousType.value
  if (oldType === 'single' || oldType === 'multiple') {
    questionTypeHistory.value[oldType] = {
      answer: editQuestion.answer,
      options: JSON.parse(JSON.stringify(editQuestion.options))
    }
  } else if (oldType === 'judgment') {
    questionTypeHistory.value.judgment = { answer: editQuestion.answer }
  } else if (oldType === 'essay') {
    questionTypeHistory.value.essay = { answer: editQuestion.answer }
  }
  
  // 记录新题�?  const newType = editQuestion.type
  previousType.value = newType
  
  // 恢复新题型的状态或初始�?  if (newType === 'single' || newType === 'multiple') {
    const history = questionTypeHistory.value[newType]
    if (history?.options && history.options.length > 0) {
      editQuestion.options = history.options.map(o => ({ ...o }))
      editQuestion.answer = history.answer || ''
    } else if (!editQuestion.options.length || editQuestion.options.every(o => !o.content)) {
      editQuestion.options = [
        { content: '', isCorrect: false },
        { content: '', isCorrect: false },
        { content: '', isCorrect: false },
        { content: '', isCorrect: false },
      ]
    }
  } else if (newType === 'judgment') {
    editQuestion.options = []
    const judgmentHistory = questionTypeHistory.value.judgment
    if (judgmentHistory?.answer && ['正确', '错误'].includes(judgmentHistory.answer)) {
      editQuestion.answer = judgmentHistory.answer
    } else {
      editQuestion.answer = '正确'
    }
  } else if (newType === 'essay') {
    // 简答题：清空其他题型的答案残留
    editQuestion.options = []
    const essayHistory = questionTypeHistory.value.essay
    if (essayHistory?.answer) {
      editQuestion.answer = essayHistory.answer
    } else {
      editQuestion.answer = ''
    }
  }
}

const setSingleAnswer = (key) => {
  editQuestion.answer = key
}

const isMultiCorrect = (idx) => {
  const key = ['A','B','C','D','E','F'][idx]
  return editQuestion.answer.includes(key)
}

const toggleEditMultiAnswer = (key) => {
  let answers = editQuestion.answer ? editQuestion.answer.split(',').map(s => s.trim()).filter(Boolean) : []
  const idx = answers.indexOf(key)
  if (idx === -1) {
    answers.push(key)
  } else {
    answers.splice(idx, 1)
  }
  editQuestion.answer = answers.join(', ')
}

const addEditOption = () => {
  editQuestion.options.push({ content: '', isCorrect: false })
}

const removeEditOption = (idx) => {
  const key = ['A','B','C','D','E','F'][idx]
  if (editQuestion.answer === key) editQuestion.answer = ''
  editQuestion.options.splice(idx, 1)
}

const restoreAnswerFromHistory = (type) => {
  if (questionTypeHistory.value[type]) {
    const saved = questionTypeHistory.value[type]
    editQuestion.answer = saved.answer || ''
    editQuestion.options = saved.options || []
  }
}

const bankFilter = reactive({
  doc: 'all',
  type: 'all',
  difficulty: 'all',
  status: 'all'
})

const bankCurrentPage = ref(1)
const bankPageSize = 10

watch(() => bankFilter.status, () => {
  bankCurrentPage.value = 1
  loadQuestionsFromDB()
})

watch([() => bankFilter.doc, () => bankFilter.type, () => bankFilter.difficulty], () => {
  bankCurrentPage.value = 1
})

const filteredBankQuestions = computed(() => {
  return questions.value.filter(q => {
    if (bankFilter.doc && String(bankFilter.doc) !== 'all' && String(bankFilter.doc) !== '全部制度') {
      const docName = String(q.doc || '')
      const filterDoc = String(bankFilter.doc || '')
      if (filterDoc && docName) {
        const match = docName.toLowerCase().includes(filterDoc.toLowerCase()) || filterDoc.toLowerCase().includes(docName.toLowerCase())
        if (!match) return false
      }
    }
    if (bankFilter.type !== 'all' && String(q.type) !== String(bankFilter.type)) {
      return false
    }
    if (bankFilter.difficulty !== 'all' && String(q.difficulty) !== String(bankFilter.difficulty)) {
      return false
    }
    return true
  })
})

const bankTotalPages = computed(() => {
  return Math.ceil(filteredBankQuestions.value.length / bankPageSize) || 1
})

const pagedBankQuestions = computed(() => {
  const start = (bankCurrentPage.value - 1) * bankPageSize
  return filteredBankQuestions.value.slice(start, start + bankPageSize)
})

const jumpPageBank = ref(null)
const handleJumpPageBank = () => {
  const page = parseInt(jumpPageBank.value)
  if (!page || page < 1 || page > bankTotalPages.value) {
    message.warning(`请输�?1~${bankTotalPages.value} 之间的页码`)
    return
  }
  bankCurrentPage.value = page
  jumpPageBank.value = null
}

const typeStats = computed(() => {
  const filtered = filteredBankQuestions.value
  return {
    total: filtered.length,
    single: filtered.filter(q => q.type === 'single').length,
    multiple: filtered.filter(q => q.type === 'multiple').length,
    judgment: filtered.filter(q => q.type === 'judgment').length,
    essay: filtered.filter(q => q.type === 'essay').length
  }
})

const paperConfig = reactive({
  name: '',
  doc: null,
  singleCount: 5,
  multipleCount: 3,
  judgmentCount: 2,
  essayCount: 0,
  easyPercent: 40,
  mediumPercent: 40,
  hardPercent: 20,
  duration: 60
})

const questions = ref([])

const papers = ref([])

const trainingData = ref({
  checkpoints: [
    { id: 1, name: '入门挑战', status: 'in-progress', score: 0, singleCount: 3, multipleCount: 0, trueFalseCount: 2, fillBlankCount: 0, subjectiveCount: 0, difficulty: 1 },
    { id: 2, name: '基础巩固', status: 'pending', score: 0, singleCount: 4, multipleCount: 2, trueFalseCount: 2, fillBlankCount: 0, subjectiveCount: 0, difficulty: 2 },
    { id: 3, name: '进阶提升', status: 'pending', score: 0, singleCount: 4, multipleCount: 3, trueFalseCount: 3, fillBlankCount: 0, subjectiveCount: 0, difficulty: 3 },
    { id: 4, name: '高级挑战', status: 'pending', score: 0, singleCount: 3, multipleCount: 3, trueFalseCount: 2, fillBlankCount: 0, subjectiveCount: 2, difficulty: 4 },
    { id: 5, name: '大师试炼', status: 'pending', score: 0, singleCount: 4, multipleCount: 4, trueFalseCount: 2, fillBlankCount: 0, subjectiveCount: 2, difficulty: 5 }
  ],
  dailyExercises: [],
  wrongQuestions: [],
})

const questionColumns = [
  { title: '题目ID', dataIndex: 'id', key: 'id' },
  { title: '题目内容', dataIndex: 'content', key: 'content' },
  { title: '题型', dataIndex: 'type', key: 'type' },
  { title: '难度', dataIndex: 'difficulty', key: 'difficulty' },
  { title: '关联制度', dataIndex: 'doc', key: 'doc' },
  { title: '状�?, dataIndex: 'status', key: 'status' },
  { title: '操作', key: 'action' },
]

const paperColumns = [
  { title: '试卷名称', dataIndex: 'name', key: 'name' },
  { title: '关联制度', dataIndex: 'doc', key: 'doc' },
  { title: '题目数量', dataIndex: 'questions', key: 'questions' },
  { title: '考试时长', dataIndex: 'duration', key: 'duration' },
  { title: '状�?, dataIndex: 'status', key: 'status' },
  { title: '操作', key: 'action' },
]

const isGenerating = ref(false)
const isGeneratingPaper = ref(false)
const generateError = ref(false)
const generateModalTitle = computed(() => {
  if (isGenerating.value) return '正在生成题目'
  if (generateError.value) return '生成失败'
  return '生成完成'
})
const generateStatusText = computed(() => {
  if (isGenerating.value) return '正在生成题目...'
  if (generateError.value) return '题目生成失败'
  return '题目生成成功�?
})

const generateDetailText = computed(() => {
  if (isGenerating.value) return 'AI 正在根据文档内容智能生成题目，请耐心等待...'
  if (generateError.value) return generateError.value
  return `已成功生�?${generatedQuestions.value.length} 道高质量题目`
})

const handleGenerateQuestions = async () => {
  if (totalQuestionCount.value <= 0) {
    message.warning('请至少选择一种题型并设置数量')
    return
  }

  if (!selectedDocData.value) {
    message.warning('请先选择制度文件')
    return
  }

  isGenerating.value = true
  generateError.value = false
  showGenerateModal.value = true
  generateProgress.value = 10

  const progressTimer = setInterval(() => {
    if (generateProgress.value < 85) {
      generateProgress.value += Math.random() * 8 + 2
    }
  }, 800)

  try {
    const activeTypes = {}
    Object.entries(questionTypeCounts).forEach(([key, val]) => {
      if (val > 0) activeTypes[key] = val
    })

    const filePath = selectedDocData.value.pdfUrl || selectedDocData.value.filePath || selectedDocData.value.path
    // 优先使用文档自带�?collectionName，否则使用用户选择的知识库
    const collectionName = selectedDocData.value.collectionName || selectedCollectionName.value || 'public_kb'

    console.log('📋 出题参数详情:')
    console.log('  - selectedDocData:', JSON.stringify(selectedDocData.value, null, 2))
    console.log('  - filePath (原始):', filePath)
    console.log('  - collectionName:', collectionName)
    console.log('  - questionTypes:', JSON.stringify(activeTypes))

    const params = {
      file_path: filePath,
      collection: collectionName,
      question_types: activeTypes,
      difficulty: difficulty.value,
      request_id: `exam_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
    }

    generateProgress.value = 40

    const result = await examAPI.generateQuestions(params)

    console.log('📥 题目生成返回结果:', result)
    console.log('📥 结果结构:', {
      hasResult: !!result,
      hasQuestions: !!(result && result.questions),
      questionsCount: result?.questions?.length || 0,
      success: result?.success,
      error: result?.error,
      errorCode: result?.errorCode,
      total: result?.total,
      allKeys: result ? Object.keys(result) : []
    })

    generateProgress.value = 90

    console.log('📥 题目生成API返回:', result)
    console.log('📥 响应结构:', {
      code: result?.code,
      message: result?.message,
      success: result?.success,
      hasData: !!result?.data
    })

    if (result && (result.code === 200 || result.success === true)) {
      generateProgress.value = 95
      console.log('�?题目生成任务已成功提交，正在加载题目列表（分页模式）...')

      questionPagination.current = 1
      questionPagination.total = 0

      await loadGeneratedQuestions(1)

      generateProgress.value = 100

      if (questionPagination.total > 0) {
        message.success(`�?题目生成成功！共 ${questionPagination.total} 道题目（每页显示 ${questionPagination.pageSize} 题）`)
      } else {
        message.info('�?题目生成任务已提交成功，但尚未查询到题目，请稍后刷新查看')
        generatedQuestions.value = []
      }
    } else if (result && result.success === false) {
      // RAG服务返回了失败响�?      const errorMsg = result.error || result.errorCode || 'RAG服务生成题目失败'
      console.error('�?RAG服务返回失败:', { error: result.error, errorCode: result.errorCode, requestId: result.requestId })
      generateError.value = `题目生成失败: ${errorMsg}`
    } else if (result && (!result.questions || result.questions.length === 0)) {
      // 成功但没有生成题�?      console.warn('⚠️ 未生成任何题目，可能原因�?)
      console.warn('  - file_path 不正确或文件不存在于向量库中')
      console.warn('  - collection 名称不正�?)
      console.warn('  - RAG服务无法从文档中提取有效内容')
      generateError.value = '未生成任何题目。请检查：1) 文件路径是否正确 2) 知识库是否已同步该文�?3) 文件内容是否可提�?
    } else if (!result) {
      // 结果为空
      console.error('�?服务器返回空结果')
      generateError.value = '服务器返回空数据，请稍后重试'
    } else {
      // 其他未知情况
      console.error('�?未知的响应格�?', result)
      generateError.value = '未生成任何题目，请调整参数后重试'
    }
  } catch (error) {
    console.error('AI出题失败:', error)
    generateProgress.value = 100
    if (error.code === 'RAG_UNAVAILABLE') {
      generateError.value = error.message
    } else if (error.message && error.message.includes('RAG服务')) {
      generateError.value = error.message
    } else {
      generateError.value = error.message || '请检查网络连�?
    }
  } finally {
    clearInterval(progressTimer)
    isGenerating.value = false
  }
}

const handleEditQuestion = (q) => {
  editQuestion.content = q.content
  editQuestion.type = q.type
  editQuestion.answer = q.answer
  editQuestion.difficulty = difficulty.value
  showEditQuestionModal.value = true
  message.info('进入题目编辑模式')
}

const importFileInput = ref(null)

const triggerImportFile = () => {
  const input = document.createElement('input')
  input.type = 'file'
  input.accept = '.json,.csv,.xlsx,.xls'
  input.onchange = (e) => {
    const file = e.target.files[0]
    if (!file) return
    if (file.name.endsWith('.json')) {
      importFromJSON(file)
    } else if (file.name.endsWith('.csv')) {
      importFromCSV(file)
    } else {
      message.warning('仅支�?JSON �?CSV 格式文件')
    }
  }
  input.click()
}

const importFromJSON = async (file) => {
  try {
    const text = await file.text()
    const data = JSON.parse(text)
    const questionsArray = Array.isArray(data) ? data : data.questions || data.data || []
    
    let importedCount = 0
    const typeMap = { single_choice: 'single', multiple_choice: 'multiple', true_false: 'judgment', fill_blank: 'essay', subjective: 'essay' }
    
    questionsArray.forEach(item => {
      const content = item.content || item.stem || ''
      let stem = '', answer = '', explanation = '', options = []
      
      if (typeof content === 'string' && content.trim().startsWith('{')) {
        try {
          const parsed = JSON.parse(content)
          stem = parsed.stem || ''
          answer = parsed.answer || ''
          explanation = parsed.explanation || ''
          options = parsed.data?.options || parsed.options || []
        } catch {
          stem = content
        }
      } else if (typeof content === 'object') {
        stem = content.stem || ''
        answer = content.answer || ''
        explanation = content.explanation || ''
        options = content.data?.options || content.options || []
      } else {
        stem = content
      }
      
      if (!stem) return
      
      const newId = questions.value.length > 0 ? Math.max(...questions.value.map(q => q.id)) + 1 : 1
      questions.value.push({
        id: newId,
        content: typeof content === 'string' ? content : JSON.stringify(content),
        stem,
        answer,
        explanation,
        options,
        type: typeMap[item.questionType] || typeMap[item.question_type] || item.type || 'single',
        difficulty: item.difficulty || 'easy',
        doc: item.doc || item.documentName || item.document_name || '',
        status: item.status || 'pending',
        expanded: false
      })
      importedCount++
    })
    
    message.success(`成功导入 ${importedCount} 道题目！`)
  } catch (e) {
    console.error('JSON 导入失败:', e)
    message.error('导入失败：文件格式错误或内容无效')
  }
}

const importFromCSV = async (file) => {
  try {
    const text = await file.text()
    const lines = text.split('\n').filter(l => l.trim())
    if (lines.length < 2) {
      message.warning('CSV 文件为空或格式不正确')
      return
    }
    
    let importedCount = 0
    for (let i = 1; i < lines.length; i++) {
      const cols = lines[i].split(',').map(c => c.trim().replace(/^"|"$/g, ''))
      if (cols.length < 3) continue
      
      const [type, difficulty, stem, answer, explanation, doc, status] = cols
      if (!stem) continue
      
      const newId = questions.value.length > 0 ? Math.max(...questions.value.map(q => q.id)) + 1 : 1
      questions.value.push({
        id: newId,
        content: stem,
        stem,
        answer: answer || '',
        explanation: explanation || '',
        options: [],
        type: type || 'single',
        difficulty: difficulty || 'easy',
        doc: doc || '',
        status: status || 'pending',
        expanded: false
      })
      importedCount++
    }
    
    message.success(`成功导入 ${importedCount} 道题目！`)
  } catch (e) {
    console.error('CSV 导入失败:', e)
    message.error('导入失败：文件格式错误或内容无效')
  }
}

const handleBatchExport = () => {
  if (questions.value.length === 0) {
    message.warning('当前没有可导出的题目')
    return
  }
  
  const exportData = questions.value.map(q => ({
    id: q.id,
    type: q.type,
    difficulty: q.difficulty,
    stem: q.stem,
    answer: q.answer,
    explanation: q.explanation,
    options: q.options,
    doc: q.doc,
    status: q.status
  }))
  
  const jsonStr = JSON.stringify(exportData, null, 2)
  const blob = new Blob([jsonStr], { type: 'application/json' })
  const url = URL.createObjectURL(blob)
  const a = document.createElement('a')
  a.href = url
  a.download = `题目导出_${new Date().toISOString().slice(0, 10)}.json`
  a.click()
  URL.revokeObjectURL(url)
  
  message.success(`成功导出 ${exportData.length} 道题目！`)
}

const handleEditBankQuestion = (record) => {
  editQuestion.id = record.id
  editQuestion.stem = record.stem || ''
  editQuestion.type = record.type || 'single'
  editQuestion.explanation = record.explanation || ''
  
  // 难度等级映射：数�?1-5 �?字符�?easy/medium/hard
  const diffValue = record.difficulty
  if (typeof diffValue === 'string') {
    editQuestion.difficulty = diffValue
  } else if (typeof diffValue === 'number') {
    editQuestion.difficulty = diffValue <= 2 ? 'easy' : diffValue <= 4 ? 'medium' : 'hard'
  } else {
    editQuestion.difficulty = 'easy'
  }
  
  // 初始化题型历史状�?  questionTypeHistory.value = {
    single: { answer: '', options: [] },
    multiple: { answer: '', options: [] },
    judgment: { answer: '' },
    essay: { answer: '' }
  }
  
  // 根据题型初始化选项和答�?  if (record.type === 'single') {
    // 单选题：初始化选项
    editQuestion.options = record.options?.length 
      ? record.options.map(o => ({ content: o.content || '', isCorrect: !!o.isCorrect }))
      : [
          { content: '', isCorrect: false },
          { content: '', isCorrect: false },
          { content: '', isCorrect: false },
          { content: '', isCorrect: false },
        ]
    
    // 设置答案：优先从 answer 字段，其次从 options �?isCorrect
    if (record.answer && ['A','B','C','D','E','F'].includes(record.answer.charAt(0))) {
      editQuestion.answer = record.answer.charAt(0)
    } else {
      const correctIdx = record.options?.findIndex(o => o.isCorrect)
      if (correctIdx !== -1) {
        editQuestion.answer = ['A','B','C','D','E','F'][correctIdx]
      }
    }
    questionTypeHistory.value.single = {
      answer: editQuestion.answer,
      options: [...editQuestion.options]
    }
  } else if (record.type === 'multiple') {
    // 多选题：初始化选项
    editQuestion.options = record.options?.length
      ? record.options.map(o => ({ content: o.content || '', isCorrect: !!o.isCorrect }))
      : [
          { content: '', isCorrect: false },
          { content: '', isCorrect: false },
          { content: '', isCorrect: false },
          { content: '', isCorrect: false },
        ]
    
    // 设置答案：从 answer 字段解析（如 "A, C" �?"AC"�?    editQuestion.answer = record.answer || ''
    if (!editQuestion.answer && record.options) {
      const correctKeys = record.options.reduce((acc, o, i) => {
        if (o.isCorrect) acc.push(['A','B','C','D','E','F'][i])
        return acc
      }, []).join(', ')
      editQuestion.answer = correctKeys
    }
    questionTypeHistory.value.multiple = {
      answer: editQuestion.answer,
      options: [...editQuestion.options]
    }
  } else if (record.type === 'judgment') {
    // 判断题：映射答案�?"正确"/"错误"
    const rawAnswer = record.answer || ''
    if (rawAnswer === '正确' || rawAnswer === '错误') {
      editQuestion.answer = rawAnswer
    } else if (rawAnswer === 'true' || rawAnswer === 'True' || rawAnswer === '1' || rawAnswer === 'T') {
      editQuestion.answer = '正确'
    } else if (rawAnswer === 'false' || rawAnswer === 'False' || rawAnswer === '0' || rawAnswer === 'F') {
      editQuestion.answer = '错误'
    } else {
      editQuestion.answer = '正确' // 默认�?    }
    questionTypeHistory.value.judgment = { answer: editQuestion.answer }
    editQuestion.options = []
  } else {
    // 简答题：直接使�?answer
    editQuestion.answer = record.answer || ''
    questionTypeHistory.value.essay = { answer: editQuestion.answer }
    editQuestion.options = []
  }
  
  previousType.value = editQuestion.type
  
  showEditQuestionModal.value = true
  message.info('进入题目编辑模式')
}

const handleSaveQuestion = () => {
  const savedData = {
    stem: editQuestion.stem,
    type: editQuestion.type,
    answer: editQuestion.answer,
    explanation: editQuestion.explanation,
    difficulty: editQuestion.difficulty,
  }
  
  if (editQuestion.type === 'single' || editQuestion.type === 'multiple') {
    savedData.options = editQuestion.options
    const correctKeys = editQuestion.options.filter(o => o.isCorrect).map((o, i) => ['A','B','C','D','E','F'][i])
    savedData.answer = correctKeys.join(editQuestion.type === 'multiple' ? ', ' : '')
  }
  
  const idx = questions.value.findIndex(q => q.id === editQuestion.id)
  if (idx !== -1) {
    questions.value[idx] = {
      ...questions.value[idx],
      stem: editQuestion.stem,
      type: editQuestion.type,
      answer: savedData.answer,
      explanation: editQuestion.explanation,
      difficulty: editQuestion.difficulty,
      options: editQuestion.options
    }
  }
  
  showEditQuestionModal.value = false
  message.success('题目修改成功�?)
}

const handleGeneratePaper = async () => {
  const totalCount = paperConfig.singleCount + paperConfig.multipleCount + paperConfig.judgmentCount + paperConfig.essayCount
  if (totalCount <= 0) {
    message.warning('请至少设置一种题型的数量')
    return
  }

  const hasFile = !!paperConfig.doc
  if (!hasFile) {
    const confirmed = await new Promise(resolve => {
      Modal.confirm({
        title: '确认生成试卷',
        content: '未选择关联制度文件，将从题库中按权限范围随机组卷。是否继续？',
        okText: '继续生成',
        cancelText: '取消',
        onOk: () => resolve(true),
        onCancel: () => resolve(false)
      })
    })
    if (!confirmed) return
  }

  isGeneratingPaper.value = true
  showGeneratePaperModal.value = true

  try {
    const fileIds = []
    if (hasFile && typeof paperConfig.doc === 'object' && paperConfig.doc.id) {
      fileIds.push(Number(paperConfig.doc.id))
    }

    const weightedDifficulty = Math.round(
      (paperConfig.easyPercent * 1 + paperConfig.mediumPercent * 3 + paperConfig.hardPercent * 5) / 100
    )

    const params = {
      single_choice_count: paperConfig.singleCount,
      multiple_choice_count: paperConfig.multipleCount,
      true_false_count: paperConfig.judgmentCount,
      subjective_count: paperConfig.essayCount,
      fill_blank_count: 0,
      include_personal: false,
      difficulty: weightedDifficulty,
      ...(fileIds.length > 0 ? { file_ids: fileIds } : {})
    }

    console.log('[智能组卷] 模式:', hasFile ? '按文件筛�? : '通用组卷(权限范围)', '参数:', params)

    const result = await examAPI.generatePaper(params)

    console.log('[智能组卷] API返回:', JSON.stringify(result))

    const rawQuestions = result.questions || []

    if (rawQuestions.length === 0) {
      message.warning(hasFile 
        ? '该制度文件暂无已审核题目。请先在「AI出题」中为该文件生成并审核通过题目�?
        : '题库中暂无符合条件的已审核题目。请先在「AI出题」中生成题目并审核通过后再组卷�?
      )
      return
    }

    const questions = rawQuestions.map((q, index) => ({
      id: index + 1,
      questionId: q.question_id,
      type: q.question_type,
      typeLabel: q.question_type_name || q.question_type,
      difficulty: q.difficulty,
      score: q.score,
      stem: q.content?.stem || '',
      options: q.content?.data?.options || q.content?.options || [],
      answer: q.content?.answer || '',
      explanation: q.content?.explanation || '',
      studentAnswer: null
    }))

    const newPaper = {
      id: Date.now(),
      paperId: result.paper_id,
      name: paperConfig.name || (hasFile ? '专项组卷' : '通用组卷'),
      doc: hasFile && typeof paperConfig.doc === 'object' ? (paperConfig.doc.title || paperConfig.doc.rawFileName || '') : (result.permission_scope || '权限范围组卷'),
      questions: questions.length,
      totalScore: result.total_score || 100,
      duration: paperConfig.duration,
      status: 'draft',
      permissionScope: result.permission_scope || '本部�?,
      questionList: questions
    }

    papers.value.unshift(newPaper)
    currentPaper.value = newPaper
    showViewPaperModal.value = true
    initPreviewState()
    message.success(`�?试卷生成成功！共 ${questions.length} 道题，可预览或直接发布`)
  } catch (error) {
    console.error('智能组卷失败:', error)
    message.error('智能组卷失败: ' + (error.message || '请检查网络连�?))
  } finally {
    isGeneratingPaper.value = false
    setTimeout(() => {
      showGeneratePaperModal.value = false
    }, 500)
  }
}

const handleViewPaper = async (record) => {
  const recordId = record.paperId || record.id
  console.log('[预览] === 开始查看试�?===')
  console.log('[预览] recordId:', recordId)
  console.log('[预览] record 数据:', JSON.stringify({
    id: record.id,
    name: record.name,
    title: record.title,
    paperId: record.paperId,
    hasQuestionList: !!record.questionList,
    questionListLength: record.questionList?.length,
    questions: record.questions,
    status: record.status
  }))

  currentViewingId.value = recordId
  isViewLoading.value = true

  try {
    // 只要有paperId且尚未加载题目数据，就从API获取详情
    const shouldFetchDetail = !!record.paperId && !Array.isArray(record.questionList) && !record.questionList?.length
    console.log('[预览] shouldFetchDetail:', shouldFetchDetail, '(paperId:', record.paperId, ')')

    if (shouldFetchDetail) {
      console.log('[预览] 从API加载完整试卷详情:', record.paperId)
      try {
        const detail = await examAPI.getAdminPaperDetail(record.paperId)
        if (detail) {
          console.log('[预览] API详情加载成功, 题目�?', detail.questions?.length || detail.questionList?.length || 0)
          currentPaper.value = { ...record, ...detail }
          showViewPaperModal.value = true
          initPreviewState()
          console.log('[预览] Modal应该已打开, showViewPaperModal=', showViewPaperModal.value)
          return
        }
      } catch (apiError) {
        console.warn('[预览] API加载失败, 使用列表数据:', apiError.message)
      }
    }

    console.log('[预览] 使用本地/列表数据, questionList长度:', record.questionList?.length)
    currentPaper.value = record
    
    console.log('[预览] 设置showViewPaperModal=true')
    showViewPaperModal.value = true
    
    await nextTick()
    console.log('[预览] nextTick�?showViewPaperModal=', showViewPaperModal.value)
    
    initPreviewState()
    console.log('[预览] initPreviewState完成, previewPaper=', !!previewPaper.value)
  } catch (error) {
    console.error('[预览] 加载试卷失败:', error)
    message.error('加载试卷详情失败，请重试: ' + (error.message || '未知错误'))
  } finally {
    isViewLoading.value = false
    currentViewingId.value = null
  }
}

// ========== 发布试卷 ==========
const isSystemAdmin = computed(() => {
  try {
    const userRole = localStorage.getItem('userRole')
    if (userRole === 'admin' || userRole === '系统管理�?) return true
    const userInfo = JSON.parse(localStorage.getItem('userInfo') || '{}')
    return userInfo.userType === 1
  } catch { return false }
})

const currentUserDeptId = computed(() => {
  try {
    const userInfo = JSON.parse(localStorage.getItem('userInfo') || '{}')
    return userInfo.deptId || userInfo.departmentId || null
  } catch { return null }
})

const fetchDepartmentList = async () => {
  try {
    const res = await departmentAPI.getDepartments()
    const body = res?.data || {}
    const list = body.data || body.records || body || []
    departmentList.value = Array.isArray(list) ? list : []
  } catch (e) {
    console.warn('获取部门列表失败:', e)
    departmentList.value = []
  }
}

const fetchUserList = async (deptId) => {
  publishUserLoading.value = true
  userList.value = []
  try {
    if (deptId) {
      const res = await departmentAPI.getDepartmentUsers(deptId)
      const body = res?.data || {}
      const list = body.data || body.records || body || []
      userList.value = Array.isArray(list) ? list : []
    } else {
      const res = await userAPI.getUsers({ pageNum: 1, pageSize: 200 })
      const body = res?.data || {}
      const list = body.data?.records || body.records || body.data || body || []
      userList.value = Array.isArray(list) ? list : []
    }
  } catch (e) {
    console.warn('获取用户列表失败:', e)
    userList.value = []
  } finally {
    publishUserLoading.value = false
  }
}

const handleOpenPublishModal = async (paper) => {
  publishingPaper.value = paper
  selectedPublishDeptId.value = isSystemAdmin.value ? null : currentUserDeptId.value
  selectedPublishUserIds.value = []
  selectedPublishExcludeUserIds.value = []
  publishUserMode.value = 'include'
  publishDeadline.value = ''
  userSearchKeyword.value = ''
  userPage.value = 1 // 重置页码

  await fetchDepartmentList()
  if (selectedPublishDeptId.value) {
    await fetchUserList(selectedPublishDeptId.value)
  }

  showPublishPaperModal.value = true
}

const handleOpenPublishModalFromView = async (paper) => {
  showViewPaperModal.value = false
  await handleOpenPublishModal(paper)
}

const removeDraftPaper = (paperId) => {
  Modal.confirm({
    title: '确认删除',
    content: '确定要删除这份草稿试卷吗？删除后无法恢复�?,
    okText: '删除',
    cancelText: '取消',
    okType: 'danger',
    onOk: () => {
      papers.value = papers.value.filter(p => p.id !== paperId)
      message.success('草稿已删�?)
    }
  })
}

// 过滤后的用户列表
const filteredUserList = computed(() => {
  if (!userSearchKeyword.value || userSearchKeyword.value.trim() === '') {
    return userList.value
  }
  const keyword = userSearchKeyword.value.trim().toLowerCase()
  return userList.value.filter(user => {
    const name = (user.nickName || user.nickname || user.realName || user.username || '').toLowerCase()
    const deptName = (user.deptName || '').toLowerCase()
    return name.includes(keyword) || deptName.includes(keyword)
  })
})

// 总页�?const userTotalPages = computed(() => Math.ceil(filteredUserList.value.length / userPageSize.value))

// 当前页的用户数据
const currentPageUsers = computed(() => {
  const start = (userPage.value - 1) * userPageSize.value
  const end = start + userPageSize.value
  return filteredUserList.value.slice(start, end)
})

// 切换页码
const changeUserPage = (page) => {
  if (page < 1 || page > userTotalPages.value) return
  userPage.value = page
}

// 生成页码数组（最多显�?个页码）
const userPageNumbers = computed(() => {
  const total = userTotalPages.value
  const current = userPage.value
  const pages = []
  
  if (total <= 9) {
    // 9页以内直接显示所�?    for (let i = 1; i <= total; i++) pages.push(i)
  } else {
    // 超过9页时，显示当前页附近的页�?+ 首尾
    if (current <= 4) {
      // �?页：显示1-7 + ... + 尾页
      for (let i = 1; i <= 7; i++) pages.push(i)
      pages.push('...')
      pages.push(total)
    } else if (current >= total - 3) {
      // �?页：显示1 + ... + 最�?�?      pages.push(1)
      pages.push('...')
      for (let i = total - 6; i <= total; i++) pages.push(i)
    } else {
      // 中间页：显示1 + ... + 当前页附�?+ ... + 尾页
      pages.push(1)
      pages.push('...')
      for (let i = current - 2; i <= current + 2; i++) pages.push(i)
      pages.push('...')
      pages.push(total)
    }
  }
  return pages
})

// 当前模式下的选中用户ID
const currentSelectedUserIds = computed(() => {
  return publishUserMode.value === 'include' 
    ? selectedPublishUserIds.value 
    : selectedPublishExcludeUserIds.value
})

// 当前已选的用户对象列表
const selectedUsers = computed(() => {
  const ids = currentSelectedUserIds.value
  return userList.value.filter(user => ids.includes(user.id || user.userId))
})

// 过滤后的列表中是否全�?const isAllSelectedInFiltered = computed(() => {
  const filtered = filteredUserList.value
  if (filtered.length === 0) return false
  const ids = currentSelectedUserIds.value
  return filtered.every(user => ids.includes(user.id || user.userId))
})

// 过滤后的列表中是否有选中
const isSomeSelectedInFiltered = computed(() => {
  const filtered = filteredUserList.value
  const ids = currentSelectedUserIds.value
  return filtered.some(user => ids.includes(user.id || user.userId))
})

const publishUserModeHint = computed(() => {
  return publishUserMode.value === 'include' ? '选择需要参加考试的用�? : '选择不需要参加考试的用�?
})

const handlePublishModeChange = (mode) => {
  publishUserMode.value = mode
  selectedPublishUserIds.value = []
  selectedPublishExcludeUserIds.value = []
  userSearchKeyword.value = ''
  userPage.value = 1 // 重置页码
}

// 切换单个用户选择
const toggleUserSelect = (user) => {
  const userId = user.id || user.userId
  const targetIds = publishUserMode.value === 'include' ? selectedPublishUserIds : selectedPublishExcludeUserIds
  const index = targetIds.value.indexOf(userId)
  if (index > -1) {
    targetIds.value.splice(index, 1)
  } else {
    targetIds.value.push(userId)
  }
}

// 全选当前过滤结�?const selectAllFiltered = () => {
  const filtered = filteredUserList.value
  const targetIds = publishUserMode.value === 'include' ? selectedPublishUserIds : selectedPublishExcludeUserIds
  filtered.forEach(user => {
    const userId = user.id || user.userId
    if (!targetIds.value.includes(userId)) {
      targetIds.value.push(userId)
    }
  })
}

// 取消全选当前过滤结�?const deselectAllFiltered = () => {
  const filtered = filteredUserList.value
  const targetIds = publishUserMode.value === 'include' ? selectedPublishUserIds : selectedPublishExcludeUserIds
  const filteredIds = filtered.map(u => u.id || u.userId)
  targetIds.value = targetIds.value.filter(id => !filteredIds.includes(id))
}

// 移除已选用�?const removeSelectedUser = (user) => {
  const userId = user.id || user.userId
  const targetIds = publishUserMode.value === 'include' ? selectedPublishUserIds : selectedPublishExcludeUserIds
  const index = targetIds.value.indexOf(userId)
  if (index > -1) {
    targetIds.value.splice(index, 1)
  }
}

// 清空所有选择
const clearAllSelected = () => {
  if (publishUserMode.value === 'include') {
    selectedPublishUserIds.value = []
  } else {
    selectedPublishExcludeUserIds.value = []
  }
}

const handlePublishDeptChange = () => {
  selectedPublishUserIds.value = []
  selectedPublishExcludeUserIds.value = []
  userPage.value = 1 // 重置页码
  userSearchKeyword.value = '' // 清空搜索
  if (selectedPublishDeptId.value) {
    fetchUserList(selectedPublishDeptId.value)
  } else {
    userList.value = []
  }
}

const handleConfirmPublish = async () => {
  const paper = publishingPaper.value
  if (!paper) return

  if (!selectedPublishDeptId.value && selectedPublishUserIds.value.length === 0 && selectedPublishExcludeUserIds.value.length === 0) {
    message.warning('请选择发布部门或指定用�?)
    return
  }

  publishLoading.value = true
  try {
    let paperId = paper.paperId || paper.id

    if (!paper._saved) {
      console.log('[发布试卷] 步骤1: 保存试卷至正式考管�?..')
      console.log('[发布试卷] 原始paper对象:', JSON.stringify(paper))
      const saveData = {
        title: paper.name || paper.paperTitle || '未命名试�?,
        description: '',
        totalScore: Number(paper.totalScore) || 100,
        duration: Number(paper.duration) || 60,
        permissionScope: paper.permissionScope || '本部�?,
        questionIds: (paper.questionList || []).map(q => String(q.questionId || q.id)).filter(Boolean)
      }
      if (!saveData.title || !saveData.title.trim()) {
        throw new Error('试卷标题不能为空')
      }
      if (!saveData.questionIds || saveData.questionIds.length === 0) {
        throw new Error('试卷题目列表为空，无法保�?)
      }
      console.log('[发布试卷] 保存参数:', JSON.stringify(saveData))
      try {
        const saveResult = await examAPI.savePaper(saveData)
        console.log('[发布试卷] 保存响应:', JSON.stringify(saveResult))
        const realId = saveResult.paperId || saveResult.paper_id || saveResult.id
        if (realId) {
          paperId = realId
          paper.paperId = realId
          paper._saved = true
        }
        console.log('[发布试卷] 保存成功, paperId:', paperId)
      } catch (saveErr) {
        console.error('[发布试卷] �?保存步骤失败:', saveErr)
        console.error('[发布试卷] 保存错误详情:', saveErr.originalError?.response?.data || saveErr)
        message.error('保存试卷失败: ' + (saveErr.message || '请检查后端服�?))
        throw saveErr
      }
    }

    console.log('[发布试卷] 步骤2: 发布试卷...')
    const publishParams = {
      deptId: selectedPublishDeptId.value ? Number(selectedPublishDeptId.value) : null,
      userIds: publishUserMode.value === 'include'
        ? selectedPublishUserIds.value.map(id => Number(id)).filter(Boolean)
        : [],
      excludeUserIds: publishUserMode.value === 'exclude'
        ? selectedPublishExcludeUserIds.value.map(id => Number(id)).filter(Boolean)
        : [],
      deadline: publishDeadline.value || null
    }
    console.log('[发布试卷] 发布参数:', JSON.stringify({ paperId, ...publishParams }))
    try {
      await examAPI.publishPaper(paperId, publishParams)
      console.log('[发布试卷] 发布成功')
    } catch (publishErr) {
      console.error('[发布试卷] �?发布步骤失败:', publishErr)
      console.error('[发布试卷] 发布错误详情(完整对象):', JSON.stringify(publishErr))
      console.error('[发布试卷] 响应状�?', publishErr?.originalError?.response?.status)
      console.error('[发布试卷] 响应数据:', publishErr?.originalError?.response?.data)
      console.error('[发布试卷] 原始消息:', publishErr?.originalError?.message || publishErr?.message || '(�?')
      const errMsg = publishErr?.originalError?.response?.data?.message
        || publishErr?.originalError?.message
        || publishErr?.message
        || '未知错误'
      message.error('发布失败: ' + errMsg)
      throw publishErr
    }

    paper.status = 'published'
    message.success('试卷发布成功�?)

    showPublishPaperModal.value = false
    publishingPaper.value = null
  } catch (error) {
    console.error('[发布试卷] 整体流程失败:', error)
  } finally {
    publishLoading.value = false
  }
}

const loadPublishedPapers = async () => {
  isLoadingPublished.value = true
  try {
    const data = await examAPI.getAdminPapers()
    publishedPapers.value = Array.isArray(data) ? data : (data?.records || [])
    console.log('[已发布试卷] 列表加载完成:', publishedPapers.value.length, '�?)
  } catch (e) {
    console.warn('获取已发布试卷列表失�?', e)
    publishedPapers.value = []
  } finally {
    isLoadingPublished.value = false
  }
}

const togglePublishedList = async () => {
  showPublishedList.value = !showPublishedList.value
  if (showPublishedList.value && publishedPapers.value.length === 0) {
    await loadPublishedPapers()
  }
}

// ========== sessionStorage 持久�?==========
const SESSION_KEY = 'exam_papers_data'

const savePapersToSession = () => {
  try {
    const data = papers.value.map(p => ({
      id: p.id,
      paperId: p.paperId,
      name: p.name,
      doc: p.doc,
      questions: p.questions,
      totalScore: p.totalScore,
      duration: p.duration,
      status: p.status,
      questionList: p.questionList
    }))
    sessionStorage.setItem(SESSION_KEY, JSON.stringify(data))
  } catch (e) {
    console.warn('保存试卷到sessionStorage失败:', e)
  }
}

const loadPapersFromSession = () => {
  try {
    const saved = sessionStorage.getItem(SESSION_KEY)
    if (saved) {
      const data = JSON.parse(saved)
      if (Array.isArray(data) && data.length > 0) {
        papers.value = data
        return true
      }
    }
  } catch (e) {
    console.warn('从sessionStorage加载试卷失败:', e)
  }
  return false
}

const paperSaveWatch = watch(papers, () => {
  savePapersToSession()
}, { deep: true })

const openEditPaperModal = (record) => {
  console.log('📂 打开编辑试卷:', record?.name || record?.paperTitle || '未知')

  editingSnapshot.value = JSON.parse(JSON.stringify(record))
  editingPaper.value = JSON.parse(JSON.stringify(record))
  selectedEditIndex.value = Math.min(0, (record.questionList?.length || 0) - 1)

  showEditPaperModal.value = true
}

const computedEditTotalScore = computed(() => {
  if (!editingPaper.value?.questionList) return 0
  return editingPaper.value.questionList.reduce((sum, q) => sum + (Number(q.score) || 0), 0)
})

const currentEditQuestion = computed(() => {
  if (!editingPaper.value?.questionList || selectedEditIndex.value < 0) return null
  return editingPaper.value.questionList[selectedEditIndex.value] || null
})

const isChoiceType = (type) => {
  if (!type) return false
  const t = String(type).toLowerCase()
  return t === 'single_choice' || t === 'multiple_choice' ||
         t === 'single' || t === 'multiple' ||
         t === '单�? || t === '多�?
}

const isTrueAnswer = (answer) => {
  if (!answer) return false
  const strAnswer = String(answer).trim().toLowerCase()
  return strAnswer === 'true' || strAnswer === '1' || strAnswer === 'yes' || strAnswer === '�?
}

const isFalseAnswer = (answer) => {
  if (!answer) return false
  const strAnswer = String(answer).trim().toLowerCase()
  return strAnswer === 'false' || strAnswer === '0' || strAnswer === 'no' || strAnswer === '�?
}

const isPreviewCorrectAnswer = (question, optionKey) => {
  if (!question || !optionKey) return false

  let answer = question.answer

  if (!answer) return false

  if (typeof answer === 'string') {
    try {
      const parsed = JSON.parse(answer)
      if (Array.isArray(parsed)) {
        answer = parsed
      }
    } catch {
      // not JSON, keep original string
    }
  }

  const type = question.type
  if (type === 'single_choice' || type === 'single' || type === '单�?) {
    return String(answer).trim() === String(optionKey).trim()
  } else if (type === 'multiple_choice' || type === 'multiple' || type === '多�?) {
    if (Array.isArray(answer)) {
      return answer.some(a => String(a).trim() === String(optionKey).trim())
    } else if (typeof answer === 'string' && answer.length > 0) {
      return answer.split(',').map(s => s.trim()).includes(String(optionKey).trim())
    }
  }

  return false
}

// ========== 查看试卷预览 - 控制函数 ==========

// 初始化预览状态（打开Modal时调用）
const initPreviewState = () => {
  previewPagination.current = 1
  previewPagination.total = currentPaper.value?.questionList?.length || 0
  expandedQuestions.value = new Set()
  isAllExpanded.value = false
  
  // 如果题目数量<=10，默认全部展开
  if (previewPagination.total <= 10) {
    isAllExpanded.value = true
    if (currentPaper.value?.questionList) {
      currentPaper.value.questionList.forEach((q, idx) => {
        expandedQuestions.value.add(`q_${idx}`)
      })
    }
  }
}

// 切换题目展开/折叠状�?const toggleQuestionExpand = (questionIndex) => {
  const key = `q_${questionIndex}`
  const newSet = new Set(expandedQuestions.value)
  
  if (newSet.has(key)) {
    newSet.delete(key)
  } else {
    newSet.add(key)
  }
  
  expandedQuestions.value = newSet
  
  // 检查是否全部展开/收起
  isAllExpanded.value = newSet.size === previewCurrentPageQuestions.value.length
}

// 判断题目是否展开
const isQuestionExpanded = (questionIndex) => {
  return expandedQuestions.value.has(`q_${questionIndex}`) || isAllExpanded.value
}

// 全部展开
const expandAllQuestions = () => {
  const newSet = new Set()
  previewCurrentPageQuestions.value.forEach((_, idx) => {
    const globalIdx = (previewPagination.current - 1) * previewPagination.pageSize + idx
    newSet.add(`q_${globalIdx}`)
  })
  expandedQuestions.value = newSet
  isAllExpanded.value = true
}

// 全部收起
const collapseAllQuestions = () => {
  expandedQuestions.value = new Set()
  isAllExpanded.value = false
}

// 分页：跳转到指定�?const goToPreviewPage = (page) => {
  if (page < 1 || page > previewTotalPages.value) return
  previewPagination.current = page
  
  // 如果之前是全部展开状态，新页面的题目也自动展开
  if (isAllExpanded.value) {
    expandAllQuestions()
  } else {
    // 否则清空展开状态（避免显示旧页面的展开数据�?    expandedQuestions.value = new Set()
  }
}

// 分页：上一�?const previewPrevPage = () => {
  goToPreviewPage(previewPagination.current - 1)
}

// 分页：下一�?const previewNextPage = () => {
  goToPreviewPage(previewPagination.current + 1)
}

// 截取题干文本（用于折叠模式显示摘要）
const truncateStem = (text, maxLength = 60) => {
  if (!text) return ''
  if (text.length <= maxLength) return text
  return text.substring(0, maxLength) + '...'
}

// 获取题目的唯一标识（用于展开/折叠�?const getQuestionKey = (index) => {
  return `q_${index}`
}

// 获取可见页码数组（复用题库管理的分页逻辑�?const getVisiblePageNumbers = (current, total) => {
  return getVisiblePages(current, total)
}

const handleCancelEditPaper = () => {
  try {
    console.log('🔄 开始执行取消编辑逻辑...')

    if (!editingPaper.value) {
      console.log('⚠️ editingPaper为空，直接关闭Modal')
      closeEditPaperModal()
      return
    }

    const hasChanges = checkEditingPaperChanged()
    console.log(`📝 检测到修改状�? ${hasChanges}`)

    if (hasChanges) {
      console.log('�?弹出确认对话�?..')
      Modal.confirm({
        title: '确认取消�?,
        content: '未保存的修改将丢�?,
        okText: '确认取消',
        cancelText: '继续编辑',
        onOk: () => {
          console.log('�?用户确认取消，开始关闭Modal...')
          editingPaper.value = editingSnapshot.value
          closeEditPaperModal()
        },
        onCancel: () => {
          console.log('↩️ 用户选择继续编辑，保持Modal打开')
        }
      })
    } else {
      console.log('�?无修改，直接关闭Modal')
      closeEditPaperModal()
    }
  } catch (error) {
    console.error('�?handleCancelEditPaper 异常:', error)
    closeEditPaperModal()
  }
}

const closeEditPaperModal = () => {
  try {
    console.log('🔒 执行关闭操作: 关闭Modal并重置状�?)

    showEditPaperModal.value = false

    editingPaper.value = null
    editingSnapshot.value = null
    selectedEditIndex.value = -1

    console.log('�?编辑试卷Modal已成功关�?)

    // 延迟清理DOM，避免影响其他模态框
    setTimeout(() => {
      try {
        const roots = document.querySelectorAll('.ant-modal-root')
        roots.forEach(el => {
          const wrap = el.querySelector('.ant-modal-wrap')
          if (wrap && wrap.style.display === 'none' && !el.querySelector('.ant-modal-wrap:not([style*="display: none"])')) {
            console.log('🧹 清理已关闭的Modal DOM')
            el.remove()
          }
        })
      } catch (cleanupErr) {
        console.warn('DOM清理时出错（可忽略）:', cleanupErr.message)
      }
    }, 300)
  } catch (err) {
    console.error('�?closeEditPaperModal 异常:', err)
    window.__editPaperDebug = { error: err, time: Date.now() }
    try { showEditPaperModal.value = false } catch(_e) {}
  }
}

// 全局调试工具
window.__forceCloseEditPaperModal = () => {
  console.log('🛠�?[调试] 强制关闭编辑试卷Modal')
  try {
    showEditPaperModal.value = false
    console.log('  -> showEditPaperModal 已设�?false')
  } catch(_e) { console.error('  -> 设置失败:', _e) }

  try {
    document.querySelectorAll('.ant-modal-root').forEach(el => {
      if (el.closest('.ep-modal-safe-wrapper') || !document.querySelector('.ep-modal-safe-wrapper')) {
        console.log('  -> 移除 Modal DOM:', el)
        el.remove()
      }
    })
  } catch(_e) { console.error('  -> DOM移除失败:', _e) }

  try {
    document.querySelectorAll('.ant-modal-mask').forEach(el => el.remove())
  } catch(_e) {}
}

window.__debugEditPaperModal = () => {
  console.log('🔍 [调试] 编辑试卷Modal状态诊�?')
  console.log('  showEditPaperModal =', showEditPaperModal.value)
  console.log('  editingPaper =', editingPaper.value ? '存在' : 'null')
  console.log('  Modal DOM elements:')
  const roots = document.querySelectorAll('.ant-modal-root')
  console.log('  .ant-modal-root 数量:', roots.length)
  roots.forEach((el, i) => {
    const isInsideWrapper = el.closest('.ep-modal-safe-wrapper')
    console.log(`    [${i}] 在包装器�? ${!!isInsideWrapper}, 可见: ${el.style.display !== 'none'}`)
  })
  const masks = document.querySelectorAll('.ant-modal-mask')
  console.log('  .ant-modal-mask 数量:', masks.length)
  return { showEditPaperModal: showEditPaperModal.value, modalElements: roots.length, maskElements: masks.length }
}

const checkEditingPaperChanged = () => {
  if (!editingPaper.value || !editingSnapshot.value) return true

  try {
    if (editingPaper.value.name !== editingSnapshot.value.name) return true

    const oldQuestions = editingSnapshot.value.questionList || []
    const newQuestions = editingPaper.value.questionList || []

    if (oldQuestions.length !== newQuestions.length) return true

    const oldJson = JSON.stringify(oldQuestions.map(q => ({ stem: q.stem, options: q.options, answer: q.answer, score: q.score, type: q.type })))
    const newJson = JSON.stringify(newQuestions.map(q => ({ stem: q.stem, options: q.options, answer: q.answer, score: q.score, type: q.type })))

    return oldJson !== newJson
  } catch (err) {
    console.warn('检查修改状态时出错，默认认为有修改:', err)
    return true
  }
}

const handleSaveEditedPaper = () => {
  try {
    console.log('💾 开始保存试�?..')

    if (!editingPaper.value?.questionList?.length) {
      console.log('⚠️ 试卷为空，无法保�?)
      message.warning('试卷至少需要一道题�?)
      return
    }

    console.log(`📊 试卷信息: ID=${editingPaper.value.id}, 题目�?${editingPaper.value.questionList.length}`)

    const paperIdx = papers.value.findIndex(p => p.id === editingPaper.value.id)
    console.log(`🔍 查找试卷索引: ${paperIdx}`)

    if (paperIdx >= 0) {
      const updatedPaper = {
        ...editingPaper.value,
        questions: editingPaper.value.questionList.length,
        totalScore: computedEditTotalScore.value
      }

      console.log('📝 保存后的 updatedPaper 结构:', {
        id: updatedPaper.id,
        name: updatedPaper.name,
        hasQuestionList: !!updatedPaper.questionList,
        questionListLength: updatedPaper.questionList?.length,
        questions: updatedPaper.questions
      })

      papers.value[paperIdx] = updatedPaper

      const paperName = editingPaper.value.name || '未命名试�?
      const questionCount = editingPaper.value.questionList.length
      const totalScore = computedEditTotalScore.value

      message.success(`试卷�?{paperName}」已保存，共 ${questionCount} �?/ ${totalScore} 分`)
      console.log(`�?保存成功: ${paperName}`)
    } else {
      console.error('�?未找到试卷ID:', editingPaper.value?.id)
      message.error('未找到对应的试卷记录')
    }

    closeEditPaperModal()
  } catch (error) {
    console.error('�?handleSaveEditedPaper 异常:', error)
    message.error('保存失败，请重试: ' + (error.message || '未知错误'))
    closeEditPaperModal()
  }
}

const handleAddQuestion = () => {
  if (!editingPaper.value.questionList) editingPaper.value.questionList = []
  const newQ = {
    id: Date.now() + Math.random(),
    questionId: `new_${Date.now()}`,
    type: 'single_choice',
    typeLabel: '单选题',
    stem: '',
    options: [
      { key: 'A', content: '' },
      { key: 'B', content: '' },
      { key: 'C', content: '' },
      { key: 'D', content: '' }
    ],
    answer: '',
    score: 5
  }
  editingPaper.value.questionList.push(newQ)
}

const handleAddQuestionAndSelect = () => {
  handleAddQuestion()
  selectedEditIndex.value = editingPaper.value.questionList.length - 1
}

const handleDeleteSelectedQuestion = () => {
  if (selectedEditIndex.value < 0 || !editingPaper.value?.questionList) return
  handleDeleteQuestion(selectedEditIndex.value)
  if (editingPaper.value.questionList.length > 0) {
    selectedEditIndex.value = Math.min(selectedEditIndex.value, editingPaper.value.questionList.length - 1)
  } else {
    selectedEditIndex.value = -1
  }
}

const handleDeleteQuestion = (qIdx) => {
  Modal.confirm({
    title: '删除题目',
    content: `确定删除�?${qIdx + 1} 题？`,
    okText: '删除',
    okType: 'danger',
    onOk: () => {
      editingPaper.value.questionList.splice(qIdx, 1)
      message.success('题目已删�?)
    }
  })
}

const moveQuestionUp = (qIdx) => {
  const list = editingPaper.value.questionList
  ;[list[qIdx], list[qIdx - 1]] = [list[qIdx - 1], list[qIdx]]
}

const moveQuestionDown = (qIdx) => {
  const list = editingPaper.value.questionList
  ;[list[qIdx], list[qIdx + 1]] = [list[qIdx + 1], list[qIdx]]
}

const addOptionToQuestion = (q) => {
  if (!q.options) q.options = []
  const keys = 'ABCDEFGH'
  const nextKey = keys[q.options.length] || String.fromCharCode(65 + q.options.length)
  q.options.push({ key: nextKey, content: '' })
}

const toggleEditMultiAnswerForPaper = (q, key) => {
  if (!Array.isArray(q.answer)) {
    q.answer = []
  }
  const idx = q.answer.indexOf(key)
  if (idx >= 0) {
    q.answer.splice(idx, 1)
  } else {
    q.answer.push(key)
  }
}

const currentCheckpointQuestions = ref([])

const handleStartCheckpoint = async (checkpoint) => {
  try {
    const params = {
      single_choice_count: checkpoint.singleCount || 0,
      multiple_choice_count: checkpoint.multipleCount || 0,
      true_false_count: checkpoint.trueFalseCount || 0,
      fill_blank_count: checkpoint.fillBlankCount || 0,
      subjective_count: checkpoint.subjectiveCount || 0,
      include_personal: false,
      difficulty: checkpoint.difficulty || 2
    }

    message.loading({ content: '正在生成闯关题目...', key: 'checkpoint' })

    const result = await examAPI.generatePaper(params)

    const rawQuestions = result.questions || []
    
    if (rawQuestions.length === 0) {
      message.destroy('checkpoint')
      message.warning('题库中暂无该关卡难度的已审核题目，请先在「AI出题」中生成并审核题�?)
      return
    }

    currentCheckpointQuestions.value = rawQuestions.map((q, index) => ({
      id: index + 1,
      questionId: q.question_id,
      type: q.question_type,
      typeLabel: q.question_type_name || q.question_type,
      difficulty: q.difficulty,
      score: q.score || 5,
      stem: q.content?.stem || '',
      options: q.content?.data?.options || q.content?.options || [],
      answer: q.content?.answer || '',
      explanation: q.content?.explanation || '',
      studentAnswer: null
    }))

    currentCheckpoint.value = {
      ...checkpoint,
      paperId: result.paper_id,
      totalScore: result.total_score || 100
    }

    showCheckpointModal.value = true
    message.destroy('checkpoint')
  } catch (error) {
    console.error('闯关题目生成失败:', error)
    message.error('题目加载失败，请重试')
  }
}

const isGrading = ref(false)
const gradeResult = ref(null)

const allCheckpointAnswered = computed(() => {
  return currentCheckpointQuestions.value.length > 0 &&
    currentCheckpointQuestions.value.every(q => q.studentAnswer != null && q.studentAnswer !== '')
})

const completedCheckpointCount = computed(() => {
  return trainingData.value.checkpoints.filter(c => c.status === 'completed').length
})

const dailyExerciseCount = computed(() => {
  return trainingData.value.dailyExercises.length || generatedQuestions.value.length
})

const isMultiCheckpointSelected = (q, key) => {
  if (!Array.isArray(q.studentAnswer)) {
    q.studentAnswer = []
  }
  return q.studentAnswer.includes(key)
}

const toggleMultiCheckpointAnswer = (q, key) => {
  if (!Array.isArray(q.studentAnswer)) {
    q.studentAnswer = []
  }
  const idx = q.studentAnswer.indexOf(key)
  if (idx >= 0) {
    q.studentAnswer.splice(idx, 1)
  } else {
    q.studentAnswer.push(key)
  }
}

const formatQuestionType = (type) => {
  const typeMap = {
    single_choice: '单选题', single: '单选题',
    multiple_choice: '多选题', multiple: '多选题',
    true_false: '判断�?, judgment: '判断�?,
    fill_blank: '填空�?,
    subjective: '简答题', essay: '简答题'
  }
  return typeMap[type] || type || '未知题型'
}

const formatAnswer = (answer, type) => {
  if (answer === null || answer === undefined || answer === '') return '暂无参考答�?
  if (Array.isArray(answer)) {
    return answer.join('�?)
  }
  try {
    const parsed = JSON.parse(answer)
    if (Array.isArray(parsed)) return parsed.join('�?)
    return parsed
  } catch {
    return answer
  }
}

const handleRetryWrongQuestion = async (item) => {
  try {
    message.loading({ content: '正在准备重练题目...', key: 'retry' })
    await examAPI.redoWrongQuestions([item.questionId])
    message.destroy('retry')
    
    const checkpoint = trainingData.value.checkpoints.find(c => c.name === item.doc)
    if (checkpoint) {
      checkpoint.status = 'in-progress'
      checkpoint.score = 0
      message.success(`已重置关卡�?{checkpoint.name}」，前往闯关模式重新挑战`)
      trainTab.value = 'checkpoint'
    } else {
      trainTab.value = 'checkpoint'
      message.info('请前往闯关模式重新挑战')
    }
  } catch (error) {
    message.destroy('retry')
    console.error('重做错题失败:', error)
    const fallbackCheckpoint = trainingData.value.checkpoints[0]
    if (fallbackCheckpoint) {
      fallbackCheckpoint.status = 'in-progress'
      fallbackCheckpoint.score = 0
      trainTab.value = 'checkpoint'
      message.info('已切换到入门挑战模式')
    } else {
      message.error('重做失败: ' + (error.message || '请稍后重�?))
    }
  }
}

const handleSubmitCheckpoint = async () => {
  if (!allCheckpointAnswered.value) return

  isGrading.value = true

  try {
    const answers = currentCheckpointQuestions.value.map(q => ({
      question_id: q.questionId || `q_${q.id}`,
      question_type: q.type,
      question_content: {
        stem: q.stem,
        data: q.options && q.options.length > 0 ? { options: q.options } : undefined,
        answer: q.answer
      },
      student_answer: q.type === 'multiple_choice' ? (q.studentAnswer || []).join(',') : q.studentAnswer,
      max_score: q.score || 5
    }))

    const result = await examAPI.gradeAnswers({
      request_id: `cp_${Date.now()}`,
      type: 'practice',
      answers
    })

    const totalScore = result.total_score || 0
    const totalMax = result.total_max_score || 0

    const checkpoint = trainingData.value.checkpoints.find(c => c.id === currentCheckpoint.value?.id)
    if (checkpoint) {
      checkpoint.status = 'completed'
      checkpoint.score = totalScore
    }

    if (result.results) {
      result.results.forEach((item, idx) => {
        if (item.correct === false) {
          const q = currentCheckpointQuestions.value[idx]
          if (q) {
            const existing = trainingData.value.wrongQuestions.find(w => w.questionId === q.questionId)
            if (!existing) {
              trainingData.value.wrongQuestions.push({
                id: Date.now() + idx,
                questionId: q.questionId,
                content: q.stem,
                type: q.type,
                typeLabel: q.typeLabel,
                options: q.options,
                correctAnswer: q.answer,
                studentAnswer: q.studentAnswer,
                score: item.score || 0,
                maxScore: item.max_score || q.score || 5,
                feedback: item.feedback || '',
                doc: currentCheckpoint.value?.name || '',
                times: 1,
                answeredAt: new Date().toISOString()
              })
            }
          }
        }
      })
    }

    gradeResult.value = { ...result, checkpointName: currentCheckpoint.value?.name }
    showAnswerModal.value = true
    message.success(`闯关完成！得�? ${totalScore}/${totalMax}`)
  } catch (error) {
    console.error('闯关提交失败:', error)
    message.error('提交失败: ' + (error.message || '请重�?))
  } finally {
    isGrading.value = false
  }
}

const scoreLevelClass = computed(() => {
  if (!gradeResult.value) return ''
  const rate = gradeResult.value.score_rate || 0
  if (rate >= 80) return 'excellent'
  if (rate >= 60) return 'good'
  return 'poor'
})

const handleSubmitDailyExercise = async () => {
  if (generatedQuestions.value.length === 0) {
    message.warning('请先生成题目')
    return
  }

  const unansweredQuestions = generatedQuestions.value.filter(q => q.studentAnswer == null || q.studentAnswer === '')
  if (unansweredQuestions.length > 0) {
    message.warning(`还有 ${unansweredQuestions.length} 道题未作答`)
    return
  }

  isGrading.value = true
  gradeResult.value = null
  showAnswerModal.value = true

  try {
    const answers = generatedQuestions.value.map(q => ({
      question_id: `q_${q.id}`,
      question_type: q.type,
      question_content: {
        stem: q.stem,
        data: q.options && q.options.length > 0 ? { options: q.options } : (q.type === 'fill_blank' ? { blank_count: 1 } : undefined),
        answer: q.answer
      },
      student_answer: q.studentAnswer,
      max_score: q.maxScore || (q.type === 'subjective' ? 20 : (q.type === 'multiple_choice' ? 10 : (q.type === 'fill_blank' ? 8 : 5)))
    }))

    const result = await examAPI.gradeAnswers({
      request_id: `grade_${Date.now()}`,
      type: 'mock_exam',
      answers
    })

    gradeResult.value = result
    message.success(`批改完成！得�? ${result.total_score || 0}/${result.total_max_score || 0}`)

    if (result.results) {
      result.results.forEach((item, idx) => {
        if (item.correct === false) {
          const q = generatedQuestions.value[idx]
          if (q) {
            const existing = trainingData.value.wrongQuestions.find(w => w.questionId === `q_${q.id}`)
            if (!existing) {
              trainingData.value.wrongQuestions.push({
                id: Date.now() + idx,
                questionId: `q_${q.id}`,
                content: q.stem,
                type: q.type,
                typeLabel: q.typeLabel,
                options: q.options,
                correctAnswer: q.answer,
                studentAnswer: q.studentAnswer,
                score: item.score || 0,
                maxScore: item.max_score || q.maxScore || 5,
                feedback: item.feedback || '',
                doc: '每日一�?,
                times: 1,
                answeredAt: new Date().toISOString()
              })
            }
          }
        }
      })
    }
  } catch (error) {
    console.error('批改失败:', error)
    gradeResult.value = null
    message.error('批改失败: ' + (error.message || '请检查网络连�?))
  } finally {
    isGrading.value = false
  }
}

const handleViewWrongQuestion = (item) => {
  currentWrongQuestion.value = item
  showAnalysisModal.value = true
}

const isMultiSelected = (q, key) => {
  if (!Array.isArray(q.studentAnswer)) {
    q.studentAnswer = []
  }
  return q.studentAnswer.includes(key)
}

const toggleMultiAnswer = (q, key) => {
  if (!Array.isArray(q.studentAnswer)) {
    q.studentAnswer = []
  }
  const idx = q.studentAnswer.indexOf(key)
  if (idx >= 0) {
    q.studentAnswer.splice(idx, 1)
  } else {
    q.studentAnswer.push(key)
  }
}

// ========== 试卷考核函数 ==========

const formatDate = (dateStr) => {
  if (!dateStr) return '-'
  try { return new Date(dateStr).toLocaleDateString('zh-CN') } catch { return dateStr }
}

const getScoreRateClass = (rate) => {
  if (rate >= 0.9) return 'rate-excellent'
  if (rate >= 0.7) return 'rate-good'
  if (rate >= 0.6) return 'rate-pass'
  return 'rate-fail'
}

const startTakingExam = async (paper) => {
  currentTakingPaper.value = paper
  currentQuestionIndex.value = 0
  takingAnswers.value = {}

  const paperId = paper.paperId || paper.paper_id || null
  currentRecordId.value = paper.recordId || paper.record_id || null

  if (currentRecordId.value) {
    console.log('[试卷考核] 已有recordId:', currentRecordId.value)
  }

  if (!paper.questionList?.length && paperId) {
    try {
      console.log('[试卷考核] 通过paperId加载题目:', paperId)
      const startResult = await examAPI.startFormalExamByPaperId(paperId)

      if (startResult) {
        if (startResult.record_id || startResult.recordId) {
          currentRecordId.value = startResult.record_id || startResult.recordId
        }
        if (startResult.questions && startResult.questions.length > 0) {
          currentTakingPaper.value = { ...currentTakingPaper.value, questionList: startResult.questions.map(q => {
            let contentObj = {}
            try {
              if (typeof q.content === 'string' && q.content.trim().startsWith('{')) {
                contentObj = JSON.parse(q.content)
              } else if (typeof q.content === 'object' && q.content !== null) {
                contentObj = q.content
              }
            } catch (e) {
              console.warn('[试卷考核] 题目content解析失败:', e.message)
            }
            return {
              questionId: q.questionId,
              type: q.questionType,
              typeLabel: formatQuestionType(q.questionType),
              difficulty: q.difficulty || 3,
              score: Number(q.score) || 5,
              stem: contentObj.stem || (typeof q.content === 'string' ? q.content : ''),
              options: contentObj.options || (contentObj.data && contentObj.data.options) || [],
              answer: '',
              explanation: ''
            }
          }) }
        } else if (!currentTakingPaper.value.questionList?.length) {
          console.warn('[试卷考核] 接口未返回题目，尝试从详情接口获�?..')
          try {
            const detail = await examAPI.getAdminPaperDetail(paperId)
            if (detail?.questions?.length) {
              currentTakingPaper.value = { ...currentTakingPaper.value, questionList: detail.questions.map(q => {
                let contentObj = {}
                try {
                  if (typeof q.content === 'string' && q.content.trim().startsWith('{')) {
                    contentObj = JSON.parse(q.content)
                  } else if (typeof q.content === 'object' && q.content !== null) {
                    contentObj = q.content
                  }
                } catch (e) {}
                return {
                  questionId: q.questionId,
                  type: q.questionType,
                  typeLabel: formatQuestionType(q.questionType),
                  difficulty: q.difficulty || 3,
                  score: Number(q.score) || 5,
                  stem: contentObj.stem || '',
                  options: contentObj.options || (contentObj.data && contentObj.data.options) || [],
                  answer: q.answer || '',
                  explanation: q.analysis || ''
                }
              }) }
            }
          } catch {}
        }
      }
    } catch (error) {
      console.warn('[试卷考核] 启动考试失败, 继续前端模式:', error.message)
    }
  }

  if (!currentTakingPaper.value.questionList?.length) {
    message.warning('该试卷暂无题目，无法开始答�?)
    return
  }

  examTab.value = 'taking'
  message.info(`开始答�? ${paper.paperTitle || paper.paper_title || paper.name || '试卷'}`)
}

const exitTakingExam = () => {
  const answeredCount = Object.keys(takingAnswers.value).length
  const totalQuestions = currentTakingPaper.value?.questionList?.length || 0

  if (answeredCount > 0 && answeredCount < totalQuestions) {
    Modal.confirm({
      title: '确认退出？',
      content: `你已回答 ${answeredCount}/${totalQuestions} 题，退出后答案将丢失。`,
      okText: '确认退�?,
      cancelText: '继续答题',
      onOk: () => {
        currentTakingPaper.value = null
        currentRecordId.value = null
        examTab.value = 'pending'
      }
    })
  } else {
    currentTakingPaper.value = null
    currentRecordId.value = null
    examTab.value = 'pending'
  }
}

const toggleMultiAnswerForTaking = (questionId, key) => {
  if (!Array.isArray(takingAnswers.value[questionId])) {
    takingAnswers.value[questionId] = []
  }
  const arr = takingAnswers.value[questionId]
  const idx = arr.indexOf(key)
  if (idx >= 0) {
    arr.splice(idx, 1)
  } else {
    arr.push(key)
  }
}

const submitExamAnswers = async () => {
  const paper = currentTakingPaper.value
  if (!paper?.questionList || paper.questionList.length === 0) return

  const unansweredCount = paper.questionList.filter(q => {
    const ans = takingAnswers.value[q.questionId]
    return ans === undefined || ans === null || ans === '' || (Array.isArray(ans) && ans.length === 0)
  }).length

  if (unansweredCount > 0) {
    const proceed = await new Promise(resolve => {
      Modal.confirm({
        title: '有未完成的题�?,
        content: `还有 ${unansweredCount} 道题未作答，确定要提交吗？`,
        okText: '强制提交',
        cancelText: '返回检�?,
        onOk: () => resolve(true),
        onCancel: () => resolve(false)
      })
    })
    if (!proceed) return
  }

  isSubmitting.value = true

  try {
    const answers = paper.questionList.map(q => ({
      question_id: String(q.questionId),
      question_type: q.type,
      question_content: {
        stem: q.stem,
        data: q.options && q.options.length > 0 ? { options: q.options } : undefined,
        answer: q.answer
      },
      student_answer: Array.isArray(takingAnswers.value[q.questionId])
        ? takingAnswers.value[q.questionId].join(',')
        : (takingAnswers.value[q.questionId] ?? ''),
      max_score: q.score || 5
    }))

    let resultData

    if (currentRecordId.value) {
      console.log('[试卷考核] 使用正式考试接口提交 (recordId):', currentRecordId.value)

      try {
        const formattedAnswers = answers.map(a => ({
          questionId: a.question_id,
          questionType: a.question_type,
          answer: a.student_answer
        }))

        resultData = await examAPI.gradeExamRecord(currentRecordId.value, formattedAnswers)
        console.log('[试卷考核] 正式考试批改完成:', resultData)
      } catch (gradeErr) {
        console.warn('[试卷考核] 正式考试接口失败，回退到通用批改接口:', gradeErr.message)

        resultData = await examAPI.gradeAnswers({
          request_id: `exam_fallback_${Date.now()}`,
          type: 'formal_exam',
          record_id: currentRecordId.value,
          answers
        })
      }
    } else {
      console.log('[试卷考核] 使用日常练习模式批改')

      resultData = await examAPI.gradeAnswers({
        request_id: `practice_${Date.now()}`,
        type: 'practice',
        answers
      })
    }

    completedResults.value.unshift({
      paperName: paper.name || paper.paperTitle || (currentRecordId.value ? '试卷' : '自测�?),
      totalScore: resultData?.total_score || resultData?.totalScore || 0,
      totalMaxScore: resultData?.total_max_score || resultData?.totalMaxScore || paper.totalScore || 100,
      scoreRate: resultData?.score_rate || resultData?.scoreRate || 0,
      results: resultData?.results || resultData?.answers || null,
      completedAt: new Date().toLocaleString(),
      paperId: paper.paperId || paper.paper_id,
      recordId: currentRecordId.value
    })

    message.success(`提交成功！得�? ${resultData?.total_score || resultData?.totalScore || '-'}/${resultData?.total_max_score || resultData?.totalMaxScore || '-'}`)

    currentTakingPaper.value = null
    currentRecordId.value = null
    takingAnswers.value = {}
    examTab.value = 'results'

  } catch (error) {
    console.error('[试卷考核] 提交失败:', error)
    message.error('提交失败: ' + (error.message || '请稍后重�?))
  } finally {
    isSubmitting.value = false
  }
}

const loadExamWrongQuestions = async () => {
  isLoadingWrongQuestions.value = true
  try {
    const result = await examAPI.getWrongQuestions({ page: 1, pageSize: 50 })
    examWrongQuestions.value = Array.isArray(result) ? result : (result.questions || result.list || result.data || [])
  } catch (error) {
    console.error('[错题本] 加载失败:', error)
    examWrongQuestions.value = []
  } finally {
    isLoadingWrongQuestions.value = false
  }
}

const redoExamWrongQuestion = async (item) => {
  try {
    await examAPI.redoWrongQuestions([item.question_id || item.id])
    message.success('已准备重做，请开始答�?)
  } catch (error) {
    console.error('重做失败:', error)
    message.info('进入答题模式')
  }
}

const viewExamWrongDetail = (item) => {
  currentWrongQuestion.value = {
    type: item.question_type || item.type,
    typeLabel: item.typeLabel,
    content: item.stem || item.content || item.question_content,
    studentAnswer: item.student_answer || item.studentAnswer,
    correctAnswer: item.correct_answer || item.answer,
    score: item.student_score || item.score,
    maxScore: item.max_score || 5,
    feedback: item.feedback,
    options: item.options
  }
  showAnalysisModal.value = true
}

const startSelftest = async () => {
  const totalCount = selftestConfig.singleCount + selftestConfig.multipleCount + selftestConfig.judgmentCount + selftestConfig.essayCount
  if (totalCount <= 0) {
    message.warning('请至少设置一种题型的数量')
    return
  }

  isGeneratingSelftest.value = true

  try {
    const params = {
      single_choice_count: selftestConfig.singleCount,
      multiple_choice_count: selftestConfig.multipleCount,
      true_false_count: selftestConfig.judgmentCount,
      subjective_count: selftestConfig.essayCount,
      fill_blank_count: 0,
      include_personal: false,
      difficulty: selftestConfig.difficulty
    }

    const result = await examAPI.generatePaper(params)

    const rawQuestions = result.questions || []
    if (rawQuestions.length === 0) {
      message.warning('题库中暂无符合条件的题目，请调整参数后重�?)
      return
    }

    const questions = rawQuestions.map((q, index) => ({
      id: index + 1,
      questionId: q.question_id,
      type: q.question_type,
      typeLabel: q.question_type_name || q.question_type,
      difficulty: q.difficulty,
      score: q.score,
      stem: q.content?.stem || '',
      options: q.content?.data?.options || q.content?.options || [],
      answer: q.content?.answer || '',
      explanation: q.content?.explanation || '',
      studentAnswer: null
    }))

    currentTakingPaper.value = {
      id: Date.now(),
      name: '自测�?- ' + new Date().toLocaleString(),
      questionList: questions,
      totalScore: result.total_score || 100
    }
    currentQuestionIndex.value = 0
    takingAnswers.value = {}
    examTab.value = 'taking'

    message.success(`自测卷生成成功，�?${questions.length} 题，开始答题！`)
  } catch (error) {
    console.error('[自测组卷] 生成失败:', error)
    message.error('生成失败: ' + (error.message || '请稍后重�?))
  } finally {
    isGeneratingSelftest.value = false
  }
}
</script>

<style scoped>
/* ========== 容器布局 ========== */
.exam-container {
  flex: 1;
  padding: 24px;
  overflow-y: auto;
  overflow-x: hidden;
  min-width: 0;
}

/* ========== 顶部标题�?========== */
.exam-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 16px 20px;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  border-radius: 12px;
  margin-bottom: 20px;
  color: white;
}

.exam-title {
  font-size: 20px;
  font-weight: bold;
  margin: 0;
  display: flex;
  align-items: center;
  gap: 8px;
}

.user-info {
  font-size: 14px;
  opacity: 0.9;
}

/* ========== 统计卡片 ========== */
.stats-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 20px;
  margin-bottom: 24px;
}

.stat-card {
  background: white;
  border-radius: 12px;
  padding: 20px;
  display: flex;
  align-items: center;
  gap: 16px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}

.stat-card::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 3px;
  background: linear-gradient(90deg, #667eea, #764ba2);
  opacity: 0;
  transition: opacity 0.2s ease;
}

.stat-card:nth-child(2)::before {
  background: linear-gradient(90deg, #11998e, #38ef7d);
}

.stat-card:nth-child(3)::before {
  background: linear-gradient(90deg, #fa709a, #fee140);
}

.stat-card:hover::before {
  opacity: 1;
}

.stat-card:hover {
  transform: translateY(-2px);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}

.stat-icon {
  font-size: 36px;
  width: 60px;
  height: 60px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: linear-gradient(135deg, #667eea15 0%, #764ba215 100%);
  border-radius: 12px;
  border: 1px solid #667eea30;
  transition: transform 0.2s ease;
}

.stat-card:nth-child(2) .stat-icon {
  background: linear-gradient(135deg, #11998e15 0%, #38ef7d15 100%);
  border-color: #11998e30;
}

.stat-card:nth-child(3) .stat-icon {
  background: linear-gradient(135deg, #fa709a15 0%, #fee14015 100%);
  border-color: #fa709a30;
}

.stat-card:hover .stat-icon {
  transform: scale(1.05);
}

.stat-info {
  flex: 1;
}

.stat-value {
  font-size: 28px;
  font-weight: 700;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
  line-height: 1.2;
}

.stat-card:nth-child(2) .stat-value {
  background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
}

.stat-card:nth-child(3) .stat-value {
  background: linear-gradient(135deg, #fa709a 0%, #fee140 100%);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
}

.stat-label {
  font-size: 13px;
  color: #666;
  margin-top: 4px;
  font-weight: 500;
}

/* 性能友好降级 */
@media (prefers-reduced-motion: reduce) {
  .stat-card,
  .stat-icon {
    transition: none !important;
  }
  
  .stat-card::before {
    display: none;
  }
}

/* ========== 卡片样式 ========== */
.exam-card {
  background: white;
  border-radius: 12px;
  padding: 24px;
  margin-bottom: 24px;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
}

.card-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 24px;
  padding-bottom: 16px;
  border-bottom: 2px solid #f0f0f0;
}

.card-header h2 {
  margin: 0;
  font-size: 18px;
  color: #333;
  display: flex;
  align-items: center;
  gap: 8px;
}

.draft-hint {
  font-size: 12px;
  color: #999;
  margin-left: auto;
  font-style: italic;
  display: flex;
  align-items: center;
  gap: 4px;
}

.hint-icon {
  cursor: help;
  font-style: normal;
}

.draft-card {
  border-left: 4px solid #faad14;
  background: linear-gradient(135deg, #fffbe6 0%, #ffffff 100%);
}

.draft-card .card-header {
  background: rgba(250, 173, 20, 0.08);
  border-radius: 8px 8px 0 0;
  padding: 16px 20px;
}

.draft-footer {
  margin-top: 16px;
  padding: 12px 16px;
  background: #fff7e6;
  border-radius: 8px;
  border: 1px solid #ffd591;
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  color: #d46b08;
}

.draft-warning {
  font-size: 16px;
}

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

/* ========== 表单样式 ========== */
.form-group {
  margin-bottom: 20px;
}

.form-label {
  display: block;
  font-size: 14px;
  font-weight: 600;
  color: #333;
  margin-bottom: 8px;
}

.form-input {
  width: 100%;
  height: 40px;
  padding: 0 14px;
  border: 1px solid #d9d9d9;
  border-radius: 8px;
  font-size: 14px;
  transition: all 0.3s;
  background: #fafafa;
  box-sizing: border-box;
}

.form-input:focus {
  outline: none;
  border-color: #1890ff;
  box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
  background: white;
}

/* ========== 统一选择器样�?========== */
.form-select {
  width: 100%;
  height: 42px;
  padding: 0 36px 0 16px;
  border: 1.5px solid #e8e8e8;
  border-radius: 10px;
  background: #fafbfc url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%238c8c8c' d='M6 8.825L1.175 4 2.238 2.938 6 6.7l3.763-3.762L10.825 4z'/%3E%3C/svg%3E") no-repeat right 14px center;
  background-size: 14px;
  font-size: 14px;
  color: #333;
  cursor: pointer;
  transition: all 0.25s ease;
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
  box-sizing: border-box;
}

.form-select:hover {
  border-color: #91d5ff;
  background-color: #fff;
  box-shadow: 0 2px 8px rgba(24, 144, 255, 0.08);
}

.form-select:focus {
  outline: none;
  border-color: #1890ff;
  box-shadow: 0 0 0 3px rgba(24, 144, 255, 0.15);
  background-color: #fff;
}

.filter-select:focus {
  outline: none;
  border-color: #1890ff;
  box-shadow: 0 0 0 3px rgba(24, 144, 255, 0.15);
  background-color: #fff;
}

/* ========== 必填标记 ========== */
.required-mark {
  color: #ff4d4f;
  margin-left: 2px;
}

/* ========== 已选知识库信息 ========== */
.collection-info {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 10px;
  padding: 8px 14px;
  background: linear-gradient(135deg, #e6f7ff 0%, #f0f9ff 100%);
  border: 1px solid #91caff;
  border-radius: 6px;
  font-size: 13px;
  color: #0050b3;
}

.collection-icon {
  font-size: 16px;
}

/* ========== 已选文件信�?========== */
.selected-file-info {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 10px;
  padding: 10px 14px;
  background: linear-gradient(135deg, #f6ffed 0%, #e6ffe6 100%);
  border: 1px solid #b7eb8f;
  border-radius: 8px;
  font-size: 13px;
}

.file-info-icon {
  font-size: 16px;
}

.file-info-name {
  font-weight: 600;
  color: #333;
  flex: 1;
}

.file-info-version {
  color: #52c41a;
  font-weight: 700;
  font-size: 12px;
  background: #f6ffed;
  padding: 2px 8px;
  border-radius: 10px;
  border: 1px solid #b7eb8f;
}

/* ========== 题型数量配置网格 ========== */
.type-count-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 12px;
}

.type-count-item {
  display: flex;
  align-items: center;
  gap: 8px;
}

.type-count-label {
  flex-shrink: 0;
  min-width: 80px;
}

.type-count-label.active {
  background: #e6f7ff;
  border-color: #91d5ff;
  color: #1890ff;
}

.type-count-input {
  width: 70px;
  text-align: center;
}

.type-count-total {
  margin-top: 14px;
  padding: 10px 16px;
  background: #fafafa;
  border-radius: 8px;
  font-size: 14px;
  color: #666;
  text-align: center;
}

.type-count-total strong {
  color: #1890ff;
  font-size: 18px;
}

/* ========== 生成弹窗样式 ========== */
.generate-modal-content {
  text-align: center;
  padding: 30px 10px;
}

.generate-spinner {
  position: relative;
  width: 80px;
  height: 80px;
  margin: 0 auto 20px;
}

.spinner-ring {
  position: absolute;
  inset: 0;
  border: 4px solid #f0f0f0;
  border-top-color: #1890ff;
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
}

.spinner-icon {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 32px;
}

.generate-status-icon {
  font-size: 56px;
  margin-bottom: 16px;
}

.generate-title {
  font-size: 18px;
  font-weight: 600;
  color: #333;
  margin-bottom: 8px;
}

.generate-detail {
  color: #666;
  font-size: 14px;
  margin-top: 16px;
  min-height: 20px;
}

/* ========== 等待动画样式（替代进度条�?========= */
.waiting-indicator {
  display: flex;
  justify-content: center;
  align-items: center;
  padding: 12px 0;
}

.waiting-dots {
  display: flex;
  gap: 8px;
}

.waiting-dots span {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: linear-gradient(135deg, #667eea, #764ba2);
  animation: waiting-bounce 1.4s ease-in-out infinite both;
}

.waiting-dots span:nth-child(1) {
  animation-delay: -0.32s;
}

.waiting-dots span:nth-child(2) {
  animation-delay: -0.16s;
}

.waiting-dots span:nth-child(3) {
  animation-delay: 0s;
}

@keyframes waiting-bounce {
  0%, 80%, 100% {
    transform: scale(0);
    opacity: 0.5;
  }
  40% {
    transform: scale(1);
    opacity: 1;
  }
}

.generate-result-summary {
  margin-top: 16px;
  padding: 14px 18px;
  background: #f6ffed;
  border-radius: 10px;
  border: 1px solid #b7eb8f;
}

.result-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-size: 14px;
}

.result-label {
  color: #666;
}

.result-value.success {
  color: #52c41a;
  font-weight: 700;
  font-size: 16px;
}

@keyframes spin {
  to { transform: rotate(360deg); }
}

/* ========== 批改结果弹窗样式 ========== */
.grade-result-content {
  padding: 10px 0;
}

.grade-result-header {
  text-align: center;
  margin-bottom: 24px;
}

.grade-score-circle {
  display: inline-flex;
  align-items: baseline;
  justify-content: center;
  gap: 4px;
  padding: 24px 36px;
  border-radius: 20px;
  border: 3px solid #e8e8e8;
  background: #fafafa;
}

.grade-score-circle.excellent {
  border-color: #52c41a;
  background: linear-gradient(135deg, #f6ffed 0%, #e6ffe6 100%);
}

.grade-score-circle.good {
  border-color: #1890ff;
  background: linear-gradient(135deg, #e6f7ff 0%, #f0f5ff 100%);
}

.grade-score-circle.poor {
  border-color: #ff4d4f;
  background: linear-gradient(135deg, #fff1f0 0%, #fff7f0 100%);
}

.score-value {
  font-size: 42px;
  font-weight: 800;
  color: #1890ff;
}

.grade-score-circle.excellent .score-value { color: #52c41a; }
.grade-score-circle.poor .score-value { color: #ff4d4f; }

.score-divider {
  font-size: 20px;
  color: #999;
  margin: 0 2px;
}

.score-max {
  font-size: 24px;
  font-weight: 500;
  color: #999;
}

.grade-rate {
  font-size: 16px;
  color: #666;
  margin-top: 8px;
  font-weight: 500;
}

.grade-results-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
  max-height: 350px;
  overflow-y: auto;
}

.grade-result-item {
  padding: 14px 16px;
  border-radius: 10px;
  border: 1px solid #e8e8e8;
  background: #fafafa;
}

.grade-result-item.correct {
  border-color: #b7eb8f;
  background: #f6ffed;
}

.grade-result-item.wrong {
  border-color: #ffa39e;
  background: #fff1f0;
}

.grade-item-header {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 6px;
}

.grade-item-num {
  font-weight: 600;
  color: #333;
  font-size: 13px;
}

.grade-item-score {
  font-weight: 700;
  color: #1890ff;
  font-size: 14px;
}

.grade-item-tag {
  font-size: 12px;
  font-weight: 600;
  padding: 2px 10px;
  border-radius: 12px;
  margin-left: auto;
}

.grade-item-tag.correct {
  background: #f6ffed;
  color: #52c41a;
  border: 1px solid #b7eb8f;
}

.grade-item-tag.wrong {
  background: #fff1f0;
  color: #ff4d4f;
  border: 1px solid #ffa39e;
}

.grade-item-tag.subjective {
  background: #e6f7ff;
  color: #1890ff;
  border: 1px solid #91d5ff;
}

.grade-item-feedback {
  font-size: 13px;
  color: #666;
  padding: 6px 0;
}

.grade-item-ai-feedback {
  font-size: 13px;
  color: #555;
  padding: 10px 14px;
  background: white;
  border-radius: 8px;
  border-left: 3px solid #1890ff;
  line-height: 1.6;
  margin-top: 6px;
}

.grade-empty {
  text-align: center;
  padding: 20px;
  color: #999;
}

/* ========== 答题相关样式 ========== */
.question-item.answered {
  border-color: #b7eb8f;
  background: linear-gradient(135deg, #f6ffed 0%, #fafafa 100%);
}

.answered-badge {
  font-size: 12px;
  color: #52c41a;
  font-weight: 600;
  background: #f6ffed;
  padding: 2px 10px;
  border-radius: 12px;
  border: 1px solid #b7eb8f;
  margin-left: auto;
}

.option-radio.selected,
.option-checkbox.selected {
  border-color: #1890ff;
  background: #e6f7ff;
}

.fill-blank-answer {
  padding: 12px 0;
}

.fill-blank-answer .form-label {
  margin-bottom: 8px;
  font-size: 13px;
  color: #666;
}

.subjective-answer {
  padding: 12px 0;
}

.subjective-answer .form-label {
  margin-bottom: 8px;
  font-size: 13px;
  color: #666;
}

.essay-textarea {
  height: auto;
  min-height: 100px;
  padding: 12px 14px;
  resize: vertical;
  font-family: inherit;
  line-height: 1.6;
}

.char-count {
  display: block;
  text-align: right;
  font-size: 12px;
  color: #999;
  margin-top: 4px;
}

.filter-select {
  width: 100%;
  height: 38px;
  padding: 0 32px 0 12px;
  border-radius: 8px;
  font-size: 13px;
  background-position: right 10px center;
  background-size: 12px;
}

.filter-select:hover {
  border-color: #91d5ff;
  box-shadow: 0 2px 6px rgba(24, 144, 255, 0.06);
}

.filter-select-sm {
  width: auto;
  min-width: 110px;
  height: 34px;
  padding: 0 28px 0 10px;
  border-radius: 8px;
  font-size: 13px;
  border: 1px solid #d9d9d9;
  background-color: white;
  cursor: pointer;
  transition: all 0.2s;
}

.filter-select-sm:hover {
  border-color: #1890ff;
}

.filter-select-sm:focus {
  border-color: #1890ff;
  outline: none;
  box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
}

/* Ant Design Select 统一样式 */
.form-select :deep(.ant-select-selector) {
  height: 42px !important;
  min-height: 42px !important;
  max-height: 42px !important;
  padding: 0 36px 0 16px !important;
  border-radius: 10px !important;
  border: 1.5px solid #e8e8e8 !important;
  background: #fafbfc !important;
  display: flex !important;
  align-items: center !important;
  justify-content: flex-start !important;
  overflow: hidden !important;
  box-sizing: border-box !important;
  line-height: 1 !important;
  transition: all 0.25s ease !important;
}

.form-select:hover :deep(.ant-select-selector) {
  border-color: #91d5ff !important;
  background: #fff !important;
  box-shadow: 0 2px 8px rgba(24, 144, 255, 0.08) !important;
}

.form-select :deep(.ant-select-focused .ant-select-selector) {
  border-color: #1890ff !important;
  box-shadow: 0 0 0 3px rgba(24, 144, 255, 0.15) !important;
  background: #fff !important;
}

.form-select :deep(.ant-select-selection-search) {
  flex: 1;
  height: 100% !important;
  display: flex !important;
  align-items: center !important;
  box-sizing: border-box !important;
}

.form-select :deep(.ant-select-selection-item) {
  height: 100% !important;
  line-height: 1 !important;
  display: flex !important;
  align-items: center !important;
  font-size: 14px;
  color: #333;
  font-weight: 500;
  box-sizing: border-box !important;
  margin: 0 !important;
  padding: 0 !important;
}

.form-select :deep(.ant-select-selection-placeholder) {
  height: 100% !important;
  line-height: 1 !important;
  display: flex !important;
  align-items: center !important;
  font-size: 14px;
  color: #bfbfbf;
  box-sizing: border-box !important;
  margin: 0 !important;
  padding: 0 !important;
}

.form-select :deep(.ant-select-arrow) {
  right: 14px !important;
  top: 50% !important;
  transform: translateY(-50%) !important;
  margin-top: 0 !important;
  color: #8c8c8c;
  font-size: 11px;
  transition: all 0.25s ease;
  position: absolute !important;
  height: 16px !important;
  width: 16px !important;
  display: flex !important;
  align-items: center !important;
  justify-content: center !important;
  overflow: hidden !important;
  box-sizing: border-box !important;
}

.form-select:hover :deep(.ant-select-arrow) {
  color: #1890ff;
  transform: translateY(-50%) rotate(180deg);
}

.form-select :deep(.ant-select-arrow svg) {
  width: 11px !important;
  height: 11px !important;
  box-sizing: border-box !important;
  overflow: visible !important;
  position: relative !important;
  z-index: 1 !important;
}

.form-row {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 20px;
}

/* ========== 复选框�?========== */
.checkbox-group {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
}

.checkbox-item {
  display: flex;
  align-items: center;
  cursor: pointer;
  padding: 8px 16px;
  background: #f5f7fa;
  border-radius: 8px;
  transition: all 0.2s;
  user-select: none;
}

.checkbox-item:hover {
  background: #e6f7ff;
}

.checkbox-item input[type="checkbox"] {
  width: 18px;
  height: 18px;
  margin-right: 8px;
  accent-color: #1890ff;
}

.checkmark {
  display: none;
}

/* ========== 按钮样式 ========== */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 10px 20px;
  border: none;
  border-radius: 8px;
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.2s;
  gap: 6px;
}

.btn-primary {
  background: linear-gradient(135deg, #1890ff 0%, #096dd9 100%);
  color: white;
}

.btn-primary:hover:not(:disabled) {
  background: linear-gradient(135deg, #40a9ff 0%, #1890ff 100%);
  transform: translateY(-2px);
  box-shadow: 0 4px 12px rgba(24, 144, 255, 0.35);
}

.btn-secondary {
  background: #f0f0f0;
  color: #666;
}

.btn-secondary:hover:not(:disabled) {
  background: #e0e0e0;
}

.btn-success {
  background: linear-gradient(135deg, #52c41a 0%, #389e0d 100%);
  color: white;
}

.btn-success:hover:not(:disabled) {
  background: linear-gradient(135deg, #73d13d 0%, #52c41a 100%);
}

.btn-info {
  background: linear-gradient(135deg, #1890ff 0%, #096dd9 100%);
  color: white;
}

.btn-info:hover:not(:disabled) {
  background: linear-gradient(135deg, #40a9ff 0%, #1890ff 100%);
}

.btn-warning {
  background: linear-gradient(135deg, #faad14 0%, #d48806 100%);
  color: white;
}

.btn-danger {
  background: linear-gradient(135deg, #ff4d4f 0%, #cf1322 100%);
  color: white;
}

.btn-sm {
  padding: 6px 14px;
  font-size: 13px;
}

.btn-xs {
  padding: 4px 10px;
  font-size: 12px;
}

.btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

/* ========== 按钮加载状态样�?========== */
.btn-loading {
  position: relative;
  background: linear-gradient(135deg, #1890ff 0%, #096dd9 100%) !important;
  color: #ffffff !important;
  cursor: wait !important;
  opacity: 0.9;
  animation: btnPulse 2s ease-in-out infinite;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 24px;
}

@keyframes btnPulse {
  0%, 100% {
    box-shadow: 0 0 0 0 rgba(24, 144, 255, 0.3);
    transform: scale(1);
  }
  50% {
    box-shadow: 0 0 4px 2px rgba(24, 144, 255, 0.15);
    transform: scale(1.01);
  }
}

.loading-dots {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  min-height: 16px;
  vertical-align: middle;
}

  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: #ffffff;
  animation: dotJump 1.4s ease-in-out infinite;
}

  animation-delay: 0s;
}

  animation-delay: 0.2s;
}

  animation-delay: 0.4s;
}

@keyframes dotJump {
  0%, 80%, 100% {
    transform: translateY(0);
    opacity: 0.4;
  }
  40% {
    transform: translateY(-4px);
    opacity: 1;
  }
}

.form-actions {
  display: flex;
  gap: 12px;
  margin-top: 24px;
  padding-top: 20px;
  border-top: 1px solid #f0f0f0;
}

/* ========== 题目列表样式 ========== */
.questions-list {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.question-item {
  background: #fafafa;
  border: 1px solid #e8e8e8;
  border-radius: 12px;
  padding: 20px;
  transition: all 0.2s;
}

.question-item:hover {
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
  border-color: #d9d9d9;
}

.question-item.answer-mode {
  background: white;
  border: 2px solid #e6f7ff;
}

.question-item.wrong-item {
  background: #fff7f0;
  border: 1px solid #ffd591;
}

.question-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 16px;
  padding-bottom: 12px;
  border-bottom: 1px dashed #e8e8e8;
}

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

.question-number {
  font-size: 13px;
  color: #999;
}

.question-content {
  font-size: 15px;
  line-height: 1.8;
  color: #333;
  margin-bottom: 16px;
  padding: 12px 16px;
  background: white;
  border-radius: 8px;
  border-left: 4px solid #1890ff;
}

/* ========== 题型标签 ========== */
.type-badge {
  display: inline-block;
  padding: 4px 12px;
  border-radius: 20px;
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
}

.type-badge.single {
  background: #e6f7ff;
  color: #1890ff;
  border: 1px solid #91d5ff;
}

.type-badge.multiple {
  background: #f6ffed;
  color: #52c41a;
  border: 1px solid #b7eb8f;
}

.type-badge.judgment {
  background: #fffbe6;
  color: #faad14;
  border: 1px solid #ffe58f;
}

.type-badge.essay {
  background: #fff1f0;
  color: #ff4d4f;
  border: 1px solid #ffa39e;
}

.status-badge {
  display: inline-block;
  padding: 4px 12px;
  border-radius: 20px;
  font-size: 12px;
  font-weight: 500;
}

.status-badge.status-info {
  background: #e6f7ff;
  color: #1890ff;
}

.status-badge.status-warning {
  background: #fffbe6;
  color: #faad14;
}

/* ========== 选项样式 ========== */
.question-options {
  margin-bottom: 16px;
}

.question-option {
  padding: 10px 16px;
  margin-bottom: 8px;
  background: white;
  border: 1px solid #e8e8e8;
  border-radius: 8px;
  display: flex;
  align-items: center;
  gap: 10px;
  transition: all 0.2s;
}

.question-option:hover {
  border-color: #1890ff;
  background: #e6f7ff;
}

.question-option.option-correct {
  background: linear-gradient(135deg, #f6ffed 0%, #d9f7be 100%);
  border-color: #b7eb8f;
  border-left: 4px solid #52c41a;
}

.question-option.option-correct .option-letter {
  color: #52c41a;
  font-weight: 700;
}

.question-option.option-correct:hover {
  border-color: #73d13d;
  background: #eaffea;
}

.correct-badge {
  margin-left: auto;
  padding: 2px 10px;
  background: #52c41a;
  color: white;
  font-size: 12px;
  font-weight: 600;
  border-radius: 10px;
  white-space: nowrap;
}

.option-letter {
  font-weight: bold;
  color: #1890ff;
  min-width: 24px;
}

.option-text {
  color: #555;
}

.answer-options {
  padding: 8px;
}

.option-radio,
.option-checkbox {
  display: flex;
  align-items: center;
  padding: 12px 16px;
  margin-bottom: 8px;
  background: white;
  border: 2px solid #e8e8e8;
  border-radius: 8px;
  cursor: pointer;
  transition: all 0.2s;
  gap: 10px;
}

.option-radio:hover,
.option-checkbox:hover {
  border-color: #1890ff;
  background: #f0faff;
}

.option-radio input[type="radio"],
.option-checkbox input[type="checkbox"] {
  width: 18px;
  height: 18px;
  accent-color: #1890ff;
}

.radio-custom {
  display: none;
}

/* ========== 答案与解�?========== */
.question-answer {
  background: linear-gradient(135deg, #f6ffed 0%, #d9f7be 100%);
  border-left: 4px solid #52c41a;
  padding: 14px 18px;
  border-radius: 8px;
  margin-bottom: 12px;
  color: #389e0d;
  font-size: 14px;
}

.question-analysis {
  background: linear-gradient(135deg, #fffbe6 0%, #fff1b8 100%);
  border-left: 4px solid #faad14;
  padding: 14px 18px;
  border-radius: 8px;
  color: #d48806;
  font-size: 14px;
  line-height: 1.7;
}

/* ========== 分页控件样式 ========== */
.pagination-wrapper {
  margin-top: 24px;
  padding: 20px;
  background: linear-gradient(135deg, #f0f5ff 0%, #e6f7ff 100%);
  border-radius: 12px;
  border: 1px solid #d6e4ff;
}

.pagination-info {
  text-align: center;
  font-size: 14px;
  color: #595959;
  margin-bottom: 16px;
  padding: 10px;
  background: white;
  border-radius: 8px;
}

.pagination-info strong {
  color: #1890ff;
  font-size: 16px;
}

.pagination-controls {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  flex-wrap: wrap;
}

.btn-pagination {
  padding: 8px 18px;
  background: white;
  border: 1px solid #d9d9d9;
  border-radius: 8px;
  cursor: pointer;
  font-size: 14px;
  font-weight: 500;
  transition: all 0.2s;
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.btn-pagination:hover:not(:disabled) {
  background: #1890ff;
  color: white;
  border-color: #1890ff;
  transform: translateY(-2px);
  box-shadow: 0 4px 12px rgba(24, 144, 255, 0.3);
}

.btn-pagination:disabled {
  opacity: 0.45;
  cursor: not-allowed;
  background: #f5f5f5;
}

.pagination-pages {
  display: flex;
  gap: 6px;
  align-items: center;
}

.btn-page {
  min-width: 38px;
  height: 38px;
  padding: 0 12px;
  background: white;
  border: 1px solid #d9d9d9;
  border-radius: 8px;
  cursor: pointer;
  font-size: 14px;
  font-weight: 500;
  transition: all 0.2s;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.btn-page:hover:not(:disabled):not(.active) {
  background: #e6f7ff;
  border-color: #1890ff;
  color: #1890ff;
  transform: translateY(-1px);
}

.btn-page.active {
  background: linear-gradient(135deg, #1890ff 0%, #096dd9 100%);
  color: white;
  border-color: #1890ff;
  box-shadow: 0 4px 12px rgba(24, 144, 255, 0.35);
  font-weight: 600;
}

.btn-page:disabled {
  cursor: default;
  background: #fafafa;
  color: #bfbfbf;
  border-color: #d9d9d9;
}

.pagination-jump {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  margin-top: 14px;
  font-size: 14px;
  color: #595959;
}

.jump-input {
  width: 64px;
  height: 34px;
  text-align: center;
  border: 1px solid #d9d9d9;
  border-radius: 8px;
  font-size: 14px;
  outline: none;
  transition: all 0.2s;
  -moz-appearance: textfield;
}

.jump-input::-webkit-outer-spin-button,
.jump-input::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

.jump-input:focus {
  border-color: #1890ff;
  box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
}

.btn-jump {
  padding: 6px 16px !important;
  background: linear-gradient(135deg, #1890ff 0%, #096dd9 100%) !important;
  color: white !important;
  border-color: #1890ff !important;
  font-weight: 500;
}

.btn-jump:hover:not(:disabled) {
  opacity: 0.85;
  transform: translateY(-1px);
  box-shadow: 0 4px 12px rgba(24, 144, 255, 0.35);
}

/* ========== 加载状态样�?========== */
.loading-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 60px 20px;
  background: linear-gradient(135deg, #f0f5ff 0%, #e6f7ff 100%);
  border-radius: 12px;
  border: 1px solid #d6e4ff;
}

.loading-spinner {
  width: 48px;
  height: 48px;
  border: 4px solid #d6e4ff;
  border-top: 4px solid #1890ff;
  border-radius: 50%;
  animation: spin 1s linear infinite;
  margin-bottom: 20px;
}

@keyframes spin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}

.loading-state p {
  font-size: 16px;
  color: #595959;
  font-weight: 500;
  margin: 0;
}

/* ========== 加载状态样�?========== */
.loading-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 60px 40px;
  background: linear-gradient(135deg, #fafafa 0%, #f5f5f5 100%);
  border-radius: 12px;
  border: 2px dashed #d9d9d9;
  text-align: center;
}

.loading-text {
  margin-top: 16px;
  font-size: 14px;
  color: #8c8c8c;
}

/* ========== 空状态样�?========== */
.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 60px 40px;
  background: linear-gradient(135deg, #fafafa 0%, #f5f5f5 100%);
  border-radius: 12px;
  border: 2px dashed #d9d9d9;
  text-align: center;
}

.empty-icon {
  font-size: 64px;
  margin-bottom: 20px;
  opacity: 0.6;
}

.empty-state h3 {
  font-size: 22px;
  color: #262626;
  margin: 0 0 12px 0;
  font-weight: 600;
}

.empty-state > p {
  font-size: 15px;
  color: #8c8c8c;
  margin: 0 0 24px 0;
}

.empty-tips {
  background: white;
  border: 1px solid #e8e8e8;
  border-radius: 10px;
  padding: 18px 24px;
  max-width: 480px;
  text-align: left;
}

.empty-tips p {
  font-size: 14px;
  color: #1890ff;
  font-weight: 600;
  margin: 0 0 10px 0;
}

.empty-tips ul {
  margin: 0;
  padding-left: 20px;
  list-style: none;
}

.empty-tips li {
  font-size: 13px;
  color: #595959;
  line-height: 2;
  position: relative;
  padding-left: 8px;
}

.empty-tips li::before {
  content: "�?;
  position: absolute;
  left: -8px;
  color: #1890ff;
  font-weight: bold;
}

/* 题目序号样式 */
.question-number {
  font-size: 12px;
  color: #8c8c8c;
  background: #f5f5f5;
  padding: 4px 10px;
  border-radius: 6px;
  margin-left: auto;
  font-weight: 500;
}

/* ========== 状态标签样�?========== */
.status-tag {
  display: inline-flex;
  align-items: center;
  font-size: 12px;
  font-weight: 500;
  padding: 4px 12px;
  border-radius: 6px;
  line-height: 1;
}

.status-tag.status-approved {
  color: #52c41a;
  background: #f6ffed;
  border: 1px solid #b7eb8f;
}

.status-tag.status-rejected {
  color: #ff4d4f;
  background: #fff2f0;
  border: 1px solid #ffccc7;
}

/* ========== 训练标签�?========== */
.train-tabs {
  display: flex;
  gap: 8px;
  margin-bottom: 20px;
  background: white;
  padding: 8px;
  border-radius: 12px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
}

.train-tab {
  flex: 1;
  padding: 12px 20px;
  border: none;
  background: transparent;
  border-radius: 8px;
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.2s;
  color: #666;
}

.train-tab:hover {
  background: #f5f7fa;
  color: #333;
}

.train-tab.active {
  background: linear-gradient(135deg, #1890ff 0%, #096dd9 100%);
  color: white;
  box-shadow: 0 4px 12px rgba(24, 144, 255, 0.25);
}

/* ========== 闯关地图 ========== */
.checkpoint-map {
  padding: 20px 0;
}

.checkpoint-item {
  position: relative;
}

.checkpoint-node {
  display: flex;
  align-items: center;
  gap: 20px;
  padding: 16px 20px;
  background: #fafafa;
  border-radius: 12px;
  border: 2px solid #e8e8e8;
  transition: all 0.2s;
  cursor: pointer;
}

.checkpoint-node:hover {
  border-color: #1890ff;
  background: #f0faff;
  transform: translateX(4px);
}

.checkpoint-item.completed .checkpoint-node {
  border-color: #52c41a;
  background: #f6ffed;
}

.checkpoint-item.in-progress .checkpoint-node {
  border-color: #1890ff;
  background: #e6f7ff;
}

.node-circle {
  width: 56px;
  height: 56px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 22px;
  font-weight: bold;
  color: white;
  background: #d9d9d9;
  flex-shrink: 0;
}

.checkpoint-item.completed .node-circle {
  background: linear-gradient(135deg, #52c41a 0%, #389e0d 100%);
  box-shadow: 0 4px 12px rgba(82, 196, 26, 0.3);
}

.checkpoint-item.in-progress .node-circle {
  background: linear-gradient(135deg, #1890ff 0%, #096dd9 100%);
  box-shadow: 0 4px 12px rgba(24, 144, 255, 0.3);
  animation: pulse 2s infinite;
}

@keyframes pulse {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.05); }
}

.node-info {
  flex: 1;
}

.node-name {
  font-size: 16px;
  font-weight: 600;
  color: #333;
  margin-bottom: 4px;
}

.node-status {
  font-size: 13px;
  color: #999;
}

.node-status.completed {
  color: #52c41a;
}

.node-status.in-progress {
  color: #1890ff;
}

.checkpoint-line {
  width: 3px;
  height: 30px;
  background: #e8e8e8;
  margin: 0 0 0 27px;
}

/* ========== 错题�?========== */
.wrong-meta {
  display: flex;
  gap: 24px;
  padding: 12px 16px;
  background: white;
  border-radius: 8px;
  margin-top: 12px;
}

.meta-item {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
}

.meta-label {
  color: #888;
}

.meta-value {
  color: #333;
  font-weight: 500;
}

.error-count {
  color: #ff4d4f;
  font-weight: bold;
}

/* ========== 空状�?========== */
.empty-state {
  text-align: center;
  padding: 60px 20px;
}

.empty-icon {
  font-size: 64px;
  margin-bottom: 16px;
}

.empty-text {
  font-size: 18px;
  color: #333;
  font-weight: 500;
  margin-bottom: 8px;
}

.empty-hint {
  font-size: 14px;
  color: #999;
}

/* ========== 响应式设�?========== */
@media (max-width: 768px) {
  .exam-container {
    padding: 12px;
  }

  .exam-header {
    flex-direction: column;
    gap: 12px;
    text-align: center;
    padding: 16px;
  }

  .stats-row {
    grid-template-columns: 1fr;
  }

  .form-row {
    grid-template-columns: 1fr;
  }

  .train-tabs {
    flex-direction: column;
  }

  .checkpoint-node {
    flex-direction: column;
    text-align: center;
  }

  .question-header {
    flex-direction: column;
    gap: 12px;
    align-items: flex-start;
  }

  .wrong-meta {
    flex-direction: column;
    gap: 8px;
  }
}

/* ========== 编辑题目弹窗样式 ========== */
.edit-options-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.edit-option-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 12px;
  background: #fafafa;
  border-radius: 8px;
  border: 1px solid #f0f0f0;
  transition: all 0.2s ease;
}

.edit-option-row.option-selected {
  background: rgba(102, 126, 234, 0.08);
  border-color: rgba(102, 126, 234, 0.3);
}

.edit-option-row .option-key {
  font-size: 14px;
  font-weight: 700;
  color: #4b5563;
  width: 20px;
  text-align: center;
  flex-shrink: 0;
}

.edit-option-row .option-input {
  flex: 1;
}

.edit-option-row .option-check {
  flex-shrink: 0;
}

.btn-remove-option {
  width: 24px;
  height: 24px;
  border: none;
  background: #ff4d4f;
  color: white;
  border-radius: 50%;
  cursor: pointer;
  font-size: 12px;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: all 0.2s ease;
  flex-shrink: 0;
}

.btn-remove-option:hover {
  background: #ff7875;
  transform: scale(1.1);
}

.btn-add-option {
  padding: 8px 16px;
  border: 1px dashed #d9d9d9;
  background: white;
  color: #666;
  border-radius: 8px;
  cursor: pointer;
  font-size: 13px;
  transition: all 0.2s ease;
}

.btn-add-option:hover {
  border-color: #667eea;
  color: #667eea;
  background: rgba(102, 126, 234, 0.05);
}

.edit-multiple-hint {
  font-size: 12px;
  color: #faad14;
  margin-top: 6px;
}

.edit-judgment-group {
  display: flex;
  gap: 16px;
}

.edit-judgment-radio {
  padding: 12px 24px;
  border: 2px solid #e8e8e8;
  border-radius: 12px;
  transition: all 0.2s ease;
  cursor: pointer;
}

.edit-judgment-radio.edit-judgment-true.ant-radio-wrapper-checked {
  border-color: #52c41a;
  background: rgba(82, 196, 26, 0.08);
}

.edit-judgment-radio.edit-judgment-false.ant-radio-wrapper-checked {
  border-color: #ff4d4f;
  background: rgba(255, 77, 79, 0.08);
}

.edit-difficulty-group {
  display: flex;
  gap: 12px;
}

.edit-difficulty-radio {
  padding: 8px 16px;
  border: 2px solid #e8e8e8;
  border-radius: 10px;
  cursor: pointer;
  transition: all 0.2s ease;
}

.edit-difficulty-radio.edit-diff-easy.ant-radio-wrapper-checked {
  border-color: #52c41a;
  background: rgba(82, 196, 26, 0.1);
}

.edit-difficulty-radio.edit-diff-medium.ant-radio-wrapper-checked {
  border-color: #faad14;
  background: rgba(250, 173, 20, 0.1);
}

.edit-difficulty-radio.edit-diff-hard.ant-radio-wrapper-checked {
  border-color: #ff4d4f;
  background: rgba(255, 77, 79, 0.1);
}
</style>

<style>
/* ========== 页面级样式（非scoped�?========== */
.exam-page {
  height: 100%;
  min-height: 100%;
  background: #f5f7fa;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  width: 100%;
  max-width: 1600px;
  margin: 0 auto;
}

/* ========== 顶部导航�?========== */
.exam-nav-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 16px 32px;
  background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%);
  color: white;
}

.nav-title {
  font-size: 22px;
  font-weight: bold;
  margin: 0;
  letter-spacing: 1px;
}

.nav-user-info {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 14px;
  opacity: 0.9;
}

.user-avatar {
  font-size: 24px;
}

/* ========== 主功能标签栏 ========== */
.main-tabs-bar {
  display: flex;
  gap: 4px;
  padding: 8px 20px;
  background: white;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
  flex-shrink: 0;
}

.main-tab-btn {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 8px 20px;
  border: none;
  background: transparent;
  border-radius: 8px;
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.25s ease;
  color: #555;
  position: relative;
}

.main-tab-btn:hover {
  background: #f0f4ff;
  color: #1890ff;
}

.main-tab-btn.active {
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  color: white;
  box-shadow: 0 4px 15px rgba(102, 126, 234, 0.35);
  transform: translateY(-2px);
}

.tab-icon {
  font-size: 18px;
}

.tab-text {
  letter-spacing: 0.5px;
}

/* ========== 题库列表样式 ========== */
.bank-list {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.bank-item {
  background: #fafafa;
  border: 1px solid #e8e8e8;
  border-radius: 12px;
  overflow: hidden;
  transition: all 0.2s;
}

.bank-item:hover {
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
  border-color: #d9d9d9;
}

.bank-item-main {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 16px 20px;
}

.bank-item-left {
  display: flex;
  flex-direction: column;
  gap: 6px;
  flex-shrink: 0;
}

.bank-item-content {
  flex: 1;
  min-width: 0;
}

.bank-question-text {
  font-size: 14px;
  color: #333;
  line-height: 1.6;
  margin-bottom: 8px;
}

.bank-meta {
  display: flex;
  gap: 16px;
  flex-wrap: wrap;
  align-items: center;
}

.meta-tag {
  font-size: 12px;
  color: #888;
  background: #f5f5f5;
  padding: 2px 10px;
  border-radius: 12px;
}

.status-dot {
  font-size: 12px;
  font-weight: 500;
}

.btn-expand-toggle {
  font-size: 12px;
  color: #667eea;
  background: transparent;
  border: 1px solid rgba(102, 126, 234, 0.3);
  padding: 2px 10px;
  border-radius: 10px;
  cursor: pointer;
  transition: all 0.2s ease;
}

.btn-expand-toggle:hover {
  background: rgba(102, 126, 234, 0.1);
  border-color: #667eea;
}

.expand-answer-enter-active,
.expand-answer-leave-active {
  transition: all 0.3s ease;
}

.expand-answer-enter-from,
.expand-answer-leave-to {
  opacity: 0;
  transform: translateY(-8px);
}

.bank-answer-section {
  margin-top: 12px;
  padding: 12px 14px;
  background: linear-gradient(135deg, #f8f9ff 0%, #f0f2ff 100%);
  border-radius: 8px;
  border: 1px solid rgba(102, 126, 234, 0.1);
}

.bank-answer-item {
  display: flex;
  gap: 6px;
  margin-bottom: 8px;
  line-height: 1.6;
}

.bank-answer-item:last-child {
  margin-bottom: 0;
}

.bank-answer-label {
  font-size: 13px;
  font-weight: 600;
  color: #4b5563;
  flex-shrink: 0;
}

.bank-answer-content {
  font-size: 13px;
  color: #374151;
}

.status-dot.approved {
  color: #52c41a;
}

.status-dot.pending {
  color: #faad14;
}

.status-dot.rejected {
  color: #ff4d4f;
}

.bank-item-actions {
  display: flex;
  gap: 6px;
  flex-shrink: 0;
}

.diff-badge {
  display: inline-block;
  padding: 2px 10px;
  border-radius: 12px;
  font-size: 11px;
  font-weight: 600;
}

.diff-badge.easy {
  background: #f6ffed;
  color: #52c41a;
  border: 1px solid #b7eb8f;
}

.diff-badge.medium {
  background: #fffbe6;
  color: #faad14;
  border: 1px solid #ffe58f;
}

.diff-badge.hard {
  background: #fff1f0;
  color: #ff4d4f;
  border: 1px solid #ffa39e;
}

/* ========== 筛选行样式 ========== */
.filter-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 16px;
}

.filter-group {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.filter-label {
  font-size: 13px;
  font-weight: 600;
  color: #555;
}

/* ========== 智能组卷 - 配置网格 ========== */
.config-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 16px;
}

.config-item {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.config-label {
  font-size: 13px;
  color: #666;
  font-weight: 500;
}

.config-input {
  width: 100%;
}

/* ========== 难度比例进度�?========== */
.difficulty-bars {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.difficulty-bar-item {
  display: flex;
  align-items: center;
  gap: 12px;
}

.bar-label {
  font-size: 13px;
  font-weight: 600;
  min-width: 40px;
  text-align: right;
}

.bar-label.easy { color: #52c41a; }
.bar-label.medium { color: #faad14; }
.bar-label.hard { color: #ff4d4f; }

.bar-track {
  flex: 1;
  height: 20px;
  background: #f0f0f0;
  border-radius: 10px;
  overflow: hidden;
  position: relative;
}

.bar-fill {
  height: 100%;
  border-radius: 10px;
  transition: width 0.3s ease;
}

.bar-fill.easy { background: linear-gradient(90deg, #95de64, #52c41a); }
.bar-fill.medium { background: linear-gradient(90deg, #ffd666, #faad14); }
.bar-fill.hard { background: linear-gradient(90deg, #ff7875, #ff4d4f); }

.bar-input {
  width: 60px;
  padding: 6px 8px;
  border: 1px solid #d9d9d9;
  border-radius: 6px;
  font-size: 13px;
  text-align: center;
}

.bar-input:focus {
  outline: none;
  border-color: #1890ff;
  box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.1);
}

.bar-unit {
  font-size: 13px;
  color: #888;
  font-weight: 500;
}

/* ========== 试卷列表样式 ========== */
.paper-list {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.paper-item {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 18px 22px;
  background: #fafafa;
  border: 1px solid #e8e8e8;
  border-radius: 12px;
  transition: all 0.2s;
}

.paper-item:hover {
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
  border-color: #d9d9d9;
  transform: translateX(4px);
}

.paper-icon {
  font-size: 36px;
  flex-shrink: 0;
}

.paper-info {
  flex: 1;
  min-width: 0;
}

.paper-name {
  font-size: 15px;
  font-weight: 600;
  color: #333;
  margin-bottom: 8px;
}

.paper-meta {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
}

.paper-actions {
  display: flex;
  gap: 6px;
  flex-shrink: 0;
}

.status-badge.status-success {
  background: #f6ffed;
  color: #52c41a;
  border: 1px solid #b7eb8f;
}

.status-badge.status-error {
  background: #fff1f0;
  color: #ff4d4f;
  border: 1px solid #ffa39e;
}

.service-warning {
  margin-bottom: 20px;
  padding: 16px 20px;
  background: linear-gradient(135deg, #fff7e6 0%, #fff1b8 100%);
  border: 1px solid #ffe58f;
  border-radius: 10px;
}

.warning-content {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
}

.warning-icon {
  font-size: 20px;
  flex-shrink: 0;
}

.warning-content span:nth-child(2) {
  flex: 1;
  color: #8c6d1f;
  font-size: 14px;
  line-height: 1.6;
  min-width: 200px;
}

.warning-content .btn {
  flex-shrink: 0;
}

/* ========== 响应式设计补�?========== */
@media (max-width: 768px) {
  .exam-nav-header {
    padding: 12px 16px;
    flex-direction: column;
    gap: 8px;
    text-align: center;
  }

  .main-tabs-bar {
    padding: 8px 12px;
    flex-wrap: wrap;
  }

  .main-tab-btn {
    padding: 10px 16px;
    font-size: 13px;
  }

  .tab-text {
    display: none;
  }

  .config-grid {
    grid-template-columns: repeat(2, 1fr);
  }

  .filter-row {
    grid-template-columns: 1fr;
  }

  .bank-item-main {
    flex-direction: column;
    align-items: flex-start;
  }

  .bank-item-left {
    flex-direction: row;
  }

  .bank-item-actions {
    width: 100%;
    justify-content: flex-end;
  }

  .paper-item {
    flex-direction: column;
    align-items: flex-start;
  }

  .paper-actions {
    width: 100%;
    justify-content: flex-end;
    margin-top: 8px;
  }

  .difficulty-bar-item {
    flex-wrap: wrap;
  }

  .bar-track {
    flex-basis: 100%;
    order: 3;
  }
}

/* ========== 查看解析弹窗 ========== */
.analysis-content { padding: 4px 0; }
.analysis-type-tag { display: flex; align-items: center; gap: 12px; margin-bottom: 12px; padding-bottom: 12px; border-bottom: 1px solid #f0f0f0; }
.analysis-score { font-size: 13px; color: #999; font-weight: 500; }
.analysis-section { margin-bottom: 16px; padding: 12px; background: #fafbfc; border-radius: 8px; border: 1px solid #f0f0f0; }
.analysis-section h6 { margin: 0 0 8px; font-size: 13px; color: #555; font-weight: 600; }
.analysis-section p { margin: 0; font-size: 14px; line-height: 1.6; color: #333; }
.student-answer { color: #ff4d4f; font-weight: 500; }
.correct-answer { color: #52c41a; font-weight: 600; }
.analysis-option { padding: 6px 10px; margin-bottom: 4px; border-radius: 6px; background: #fff; border: 1px solid #e8e8e8; transition: all 0.15s; }
.analysis-option.option-correct { background: #f6ffed; border-color: #b7eb8f; }
.correct-mark { float: right; color: #52c41a; font-size: 12px; font-weight: 600; }

/* ========== 试卷考核样式 ========== */
.sub-tabs-bar { display: flex; gap: 4px; margin-bottom: 20px; padding: 0 4px; flex-wrap: wrap; }
.sub-tab {
  padding: 8px 16px; border: 1.5px solid #e8e8e8; border-radius: 8px; background: #fff;
  font-size: 13px; cursor: pointer; transition: all 0.2s; color: #666; display: inline-flex; align-items: center; gap: 6px;
}
.sub-tab:hover:not(:disabled) { border-color: #1890ff; color: #1890ff; }
.sub-tab.active { background: linear-gradient(135deg, #667eea, #764ba2); color: #fff; border-color: transparent; }
.sub-tab:disabled { opacity: 0.4; cursor: not-allowed; }
.tab-badge {
  background: #ff4d4f; color: #fff; font-size: 11px; min-width: 18px; height: 18px;
  border-radius: 9px; display: inline-flex; align-items: center; justify-content: center; padding: 0 5px; font-weight: 600;
}

/* 待考试卡片 */
.papers-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 16px; margin-top: 16px; }
.paper-card {
  background: #fff; border: 1.5px solid #e8e8e8; border-radius: 12px; padding: 20px;
  cursor: pointer; transition: all 0.25s; position: relative; overflow: hidden;
}
.paper-card:hover { border-color: #1890ff; box-shadow: 0 6px 20px rgba(24,144,255,0.12); transform: translateY(-2px); }
.paper-card-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; }
.paper-status-badge {
  padding: 3px 10px; border-radius: 20px; font-size: 12px; font-weight: 600;
}
.status-pending { background: #fff7e6; color: #fa8c16; }
.paper-score { font-size: 18px; font-weight: 700; color: #1890ff; }
.paper-card-title { font-size: 16px; font-weight: 600; color: #262626; margin: 0 0 12px; }
.paper-card-meta { display: flex; gap: 16px; font-size: 13px; color: #999; flex-wrap: wrap; }
.paper-card-action { margin-top: 14px; text-align: right; }
.action-hint { font-size: 13px; color: #1890ff; font-weight: 500; }

/* 答题页面 */
/* ========== 自测卷答题页�?- 专业级UI设计 ========== */
.exam-taking-container {
  max-width: 1100px;
  margin: 0 auto;
  padding: 24px 16px;
}

.taking-header {
  display: flex; align-items: center; justify-content: space-between;
  padding: 20px 28px;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  border-radius: 16px; color: #fff;
  margin-bottom: 24px;
  box-shadow: 0 8px 24px rgba(102, 126, 234, 0.3);
  position: relative; overflow: hidden;
}
.taking-header::before {
  content: '';
  position: absolute; top: -50%; right: -10%;
  width: 300px; height: 300px;
  background: radial-gradient(circle, rgba(255,255,255,0.1) 0%, transparent 70%);
  border-radius: 50%;
}
.taking-header .btn-outline {
  background: rgba(255,255,255,0.2);
  color: #fff;
  border-color: rgba(255,255,255,0.4);
  padding: 8px 18px;
  border-radius: 8px;
  font-weight: 500;
  font-size: 14px;
  transition: all 0.25s ease;
  backdrop-filter: blur(10px);
}
.taking-header .btn-outline:hover:not(:disabled) {
  background: rgba(255,255,255,0.3);
  transform: translateY(-1px);
  box-shadow: 0 4px 12px rgba(0,0,0,0.15);
}
.taking-title {
  font-weight: 700;
  font-size: 18px;
  letter-spacing: 0.5px;
  position: relative; z-index: 1;
}
.taking-progress {
  font-size: 15px;
  opacity: 0.95;
  font-weight: 500;
  background: rgba(255,255,255,0.15);
  padding: 6px 16px;
  border-radius: 20px;
  backdrop-filter: blur(10px);
}
.taking-body {
  background: #fff;
  border: 1px solid #e8e8e8;
  border-radius: 16px;
  padding: 36px 40px;
  margin-bottom: 20px;
  min-height: 380px;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.04);
  transition: box-shadow 0.3s ease;
}
.taking-body:hover {
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
}

.question-type-tag {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-bottom: 20px;
}
.type-badge {
  display: inline-block;
  padding: 6px 16px;
  border-radius: 20px;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.5px;
  text-transform: uppercase;
}
.type-badge.single_choice, .type-badge.single { background: linear-gradient(135deg, #e6f7ff, #bae0ff); color: #0958d9; }
.type-badge.multiple_choice, .type-badge.multiple { background: linear-gradient(135deg, #f9f0ff, #efdbff); color: #531dab; }
.type-badge.true_false, .type-badge.judgment { background: linear-gradient(135deg, #fff7e6, #ffe7ba); color: #ad6800; }
.type-badge.fill_blank { background: linear-gradient(135deg, #fcffe6, #eaff8a); color: #389e0d; }
.type-badge.subjective, .type-badge.essay { background: linear-gradient(135deg, #fff0f6, #ffadd2); color: #c41d7f; }

.question-score {
  font-size: 15px;
  color: #faad14;
  font-weight: 600;
}

.question-stem {
  font-size: 19px;
  line-height: 1.8;
  color: #1a1a2e;
  margin-bottom: 28px;
  font-weight: 500;
  letter-spacing: 0.3px;
}
.options-list {
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.options-list.options-inline {
  flex-direction: row;
  gap: 20px;
}

.option-item {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 16px 20px;
  border: 2px solid #e8e8e8;
  border-radius: 12px;
  cursor: pointer;
  transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
  font-size: 16px;
  background: #fafbfc;
  position: relative;
  overflow: hidden;
}
.option-item::before {
  content: '';
  position: absolute; left: 0; top: 0; bottom: 0;
  width: 4px; background: transparent;
  transition: all 0.25s ease;
}
.option-item:hover {
  border-color: #91d5ff;
  background: #f0f9ff;
  transform: translateX(4px);
  box-shadow: 0 4px 12px rgba(24, 144, 255, 0.1);
}
.option-item:hover::before { background: #91d5ff; }

.option-item.selected {
  border-color: #1890ff;
  background: linear-gradient(135deg, #e6f7ff 0%, #f0f9ff 100%);
  box-shadow: 0 4px 16px rgba(24, 144, 255, 0.15);
}
.option-item.selected::before { background: #1890ff; }

.option-item.multi.selected {
  border-color: #722ed1;
  background: linear-gradient(135deg, #f9f0ff 0%, #fdf4ff 100%);
  box-shadow: 0 4px 16px rgba(114, 46, 209, 0.15);
}
.option-item.multi.selected::before { background: #722ed1; }
.option-key {
  width: 32px; height: 32px;
  border-radius: 50%;
  background: linear-gradient(135deg, #f0f0f0, #e0e0e0);
  display: inline-flex;
  align-items: center; justify-content: center;
  font-weight: 700; font-size: 14px;
  color: #666; flex-shrink: 0;
  transition: all 0.25s ease;
  border: 2px solid transparent;
}
.option-item.selected .option-key {
  background: linear-gradient(135deg, #1890ff, #0958d9);
  color: #fff;
  box-shadow: 0 4px 8px rgba(24, 144, 255, 0.3);
  transform: scale(1.05);
}
.option-item.multi.selected .option-key {
  background: linear-gradient(135deg, #722ed1, #531dab);
  color: #fff;
  box-shadow: 0 4px 8px rgba(114, 46, 209, 0.3);
  transform: scale(1.05);
}

.option-text {
  flex: 1;
  font-size: 16px;
  line-height: 1.6;
  color: #333;
}
.fill-blank-area textarea, .subjective-area textarea {
  width: 100%;
  padding: 16px 20px;
  border: 2px solid #e8e8e8;
  border-radius: 12px;
  font-size: 16px;
  line-height: 1.7;
  resize: vertical;
  font-family: inherit;
  outline: none;
  transition: all 0.25s ease;
  background: #fafbfc;
}
.fill-blank-area textarea:focus, .subjective-area textarea:focus {
  border-color: #1890ff;
  box-shadow: 0 0 0 4px rgba(24,144,255,0.1);
  background: #fff;
}
.taking-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 20px 28px;
  background: linear-gradient(135deg, #fafbfc 0%, #f5f7fa 100%);
  border: 1px solid #e8e8e8;
  border-radius: 16px;
  gap: 16px;
  flex-wrap: wrap;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
}

.taking-footer .btn {
  padding: 10px 24px;
  font-size: 15px;
  font-weight: 600;
  border-radius: 10px;
  transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
}
.taking-footer .btn-outline {
  background: #fff;
  color: #555;
  border: 2px solid #d9d9d9;
}
.taking-footer .btn-outline:hover:not(:disabled) {
  border-color: #1890ff;
  color: #1890ff;
  transform: translateY(-2px);
  box-shadow: 0 4px 12px rgba(24, 144, 255, 0.15);
}
.taking-footer .btn-primary {
  background: linear-gradient(135deg, #1890ff, #0958d9);
  border: none;
  color: #fff;
}
.taking-footer .btn-primary:hover:not(:disabled) {
  transform: translateY(-2px);
  box-shadow: 0 6px 20px rgba(24, 144, 255, 0.35);
}
.taking-footer .btn-success {
  background: linear-gradient(135deg, #52c41a, #389e0d);
  border: none;
  color: #fff;
}
.taking-footer .btn-success:hover:not(:disabled) {
  transform: translateY(-2px);
  box-shadow: 0 6px 20px rgba(82, 196, 26, 0.35);
}

/* ========== ���⿨��� ========== */
.taking-layout { display: flex; gap: 20px; align-items: flex-start; }
.answer-card-sidebar { width: 220px; flex-shrink: 0; background: #fff; border: 1px solid #e8e8e8; border-radius: 12px; padding: 16px; position: sticky; top: 16px; max-height: calc(100vh - 200px); overflow-y: auto; box-shadow: 0 2px 8px rgba(0,0,0,0.04); }
.answer-card-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 14px; padding-bottom: 10px; border-bottom: 1px solid #f0f0f0; }
.answer-card-title { font-size: 15px; font-weight: 700; color: #262626; }
.answer-card-stats { font-size: 12px; color: #8c8c8c; background: #f5f5f5; padding: 2px 10px; border-radius: 10px; }
.answer-card-grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 8px; }
.answer-card-item { position: relative; display: flex; align-items: center; justify-content: center; height: 36px; border-radius: 8px; cursor: pointer; font-size: 13px; font-weight: 600; color: #595959; background: #fafafa; border: 2px solid #e8e8e8; transition: all 0.2s cubic-bezier(0.4,0,0.2,1); user-select: none; }
.answer-card-item:hover { border-color: #91d5ff; background: #e6f7ff; transform: translateY(-1px); }
.answer-card-item.answered { background: #f6ffed; border-color: #b7eb8f; color: #389e0d; }
.answer-card-item.answered:hover { background: #d9f7be; border-color: #95de64; }
.answer-card-item.active { background: #1890ff; border-color: #1890ff; color: #fff; box-shadow: 0 4px 12px rgba(24,144,255,0.35); transform: translateY(-1px); }
.answer-card-item.active:hover { background: #40a9ff; border-color: #40a9ff; }
.card-item-num { line-height: 1; }
.card-check-mark { position: absolute; top: -4px; right: -4px; font-size: 10px; color: #52c41a; background: #fff; border-radius: 50%; width: 16px; height: 16px; display: flex; align-items: center; justify-content: center; box-shadow: 0 1px 3px rgba(0,0,0,0.15); }
.answer-card-item.active .card-check-mark { color: #fff; background: rgba(255,255,255,0.25); }
.taking-content-area { flex: 1; min-width: 0; }
.footer-center-info { display: flex; align-items: center; justify-content: center; }
.footer-question-num { font-size: 14px; color: #8c8c8c; font-weight: 500; }
.answer-card-sidebar::-webkit-scrollbar { width: 4px; }
.answer-card-sidebar::-webkit-scrollbar-track { background: transparent; }
.answer-card-sidebar::-webkit-scrollbar-thumb { background: #d9d9d9; border-radius: 2px; }

/* ========== 响应式适配 ========== */

/* 平板设备�?68px - 1024px�?*/
@media (max-width: 1024px) {
  .exam-taking-container {
    max-width: 95%;
    padding: 16px;
  }

  .taking-header {
    padding: 16px 20px;
    flex-wrap: wrap;
    gap: 12px;
  }

  .taking-title {
    font-size: 16px;
    order: -1;
    width: 100%;
    text-align: center;
  }

  .taking-body {
    padding: 28px 24px;
    min-height: 320px;
  }

  .question-stem {
    font-size: 17px;
    margin-bottom: 24px;
  }

  .option-item {
    padding: 14px 16px;
    font-size: 15px;
  }

  .taking-footer {
    padding: 16px 20px;
  }
}

/* 手机设备�? 768px�?*/
@media (max-width: 768px) {
  .exam-taking-container {
    max-width: 100%;
    padding: 12px;
  }

  .taking-header {
    padding: 14px 16px;
    border-radius: 12px;
    margin-bottom: 16px;
  }

  .taking-header::before {
    display: none;
  }

  .taking-title {
    font-size: 15px;
  }

  .taking-progress {
    font-size: 13px;
    padding: 4px 12px;
  }

  .taking-body {
    padding: 24px 20px;
    min-height: 280px;
    border-radius: 12px;
  }

  .question-type-tag {
    gap: 10px;
    margin-bottom: 16px;
  }

  .type-badge {
    padding: 5px 12px;
    font-size: 12px;
  }

  .question-score {
    font-size: 13px;
  }

  .question-stem {
    font-size: 16px;
    line-height: 1.7;
    margin-bottom: 20px;
  }

  .options-list {
    gap: 10px;
  }

  .options-list.options-inline {
    flex-direction: column;
    gap: 10px;
  }

  .option-item {
    padding: 12px 14px;
    font-size: 14px;
    border-radius: 10px;
    gap: 12px;
  }

  .option-key {
    width: 28px; height: 28px;
    font-size: 13px;
  }

  .option-text {
    font-size: 14px;
  }

  .fill-blank-area textarea,
  .subjective-area textarea {
    padding: 14px 16px;
    font-size: 15px;
    border-radius: 10px;
  }

  .taking-footer {
    padding: 14px 16px;
    border-radius: 12px;
    gap: 12px;
  }

  .taking-footer .btn {
    padding: 8px 18px;
    font-size: 14px;
  }

    gap: 6px;
  }

    width: 10px; height: 10px;
  }
}

/* 小屏手机�? 480px�?*/
@media (max-width: 480px) {
  .taking-header .btn-outline {
    padding: 6px 12px;
    font-size: 13px;
  }

  .taking-body {
    padding: 20px 16px;
    min-height: 250px;
  }

  .question-stem {
    font-size: 15px;
  }

  .option-item {
    padding: 10px 12px;
    font-size: 13px;
  }

  .taking-footer {
    flex-direction: column;
    text-align: center;
  }

  .taking-footer .btn {
    width: 100%;
  }
}

/* ========== 交互增强效果 ========== */

/* 页面容器淡入动画 */
.exam-taking-container {
  animation: fadeInUp 0.5s ease-out;
}
@keyframes fadeInUp {
  from {
    opacity: 0;
    transform: translateY(20px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* 按钮点击按压效果 */
.taking-footer .btn:active:not(:disabled) {
  transform: translateY(0) scale(0.98);
}

.option-item:active:not(.selected) {
  transform: translateX(4px) scale(0.99);
}

/* 选项选中时的脉冲动画 */
.option-item.selected {
  animation: selectPulse 0.3s ease-out;
}
@keyframes selectPulse {
  0% { box-shadow: 0 0 0 0 rgba(24, 144, 255, 0.4); }
  100% { box-shadow: 0 4px 16px rgba(24, 144, 255, 0.15); }
}

.option-item.multi.selected {
  animation: multiSelectPulse 0.3s ease-out;
}
@keyframes multiSelectPulse {
  0% { box-shadow: 0 0 0 0 rgba(114, 46, 209, 0.4); }
  100% { box-shadow: 0 4px 16px rgba(114, 46, 209, 0.15); }
}

/* 导航点点击效�?*/
  transform: scale(1.2);
}

/* 禁用状态的视觉反馈 */
.btn:disabled,
.option-item.disabled {
  opacity: 0.5;
  cursor: not-allowed;
  pointer-events: none;
}

/* 输入框获得焦点时的光晕效�?*/
.fill-blank-area textarea:focus::placeholder,
.subjective-area textarea:focus::placeholder {
  color: #bfbfbf;
}

/* 成绩�?*/
.result-card {
  background: #fff; border: 1.5px solid #e8e8e8; border-radius: 12px; padding: 24px; margin-bottom: 16px;
}
.result-header { display: flex; align-items: center; gap: 20px; margin-bottom: 8px; flex-wrap: wrap; }
.result-header h3 { margin: 0; font-size: 17px; flex: 1; }
.result-score-big { display: flex; align-items: baseline; gap: 4px; }
.score-num { font-size: 32px; font-weight: 800; color: #1890ff; }
.score-unit { font-size: 14px; color: #999; }
.rate-badge {
  padding: 4px 12px; border-radius: 20px; font-size: 13px; font-weight: 600;
}
.rate-excellent { background: #f6ffed; color: #52c41a; }
.rate-good { background: #e6f7ff; color: #1890ff; }
.rate-pass { background: #fffbe6; color: #faad14; }
.rate-fail { background: #fff2f0; color: #ff4d4f; }
.result-time { font-size: 13px; color: #bbb; margin-bottom: 16px; }
.result-questions { display: flex; flex-direction: column; gap: 6px; }
.result-question-row {
  display: flex; align-items: center; gap: 10px; padding: 8px 12px; border-radius: 8px; font-size: 13px;
}
.result-question-row.correct { background: #f6ffed; }
.result-question-row.wrong { background: #fff2f0; }
.rq-status { font-size: 16px; }
.rq-index { font-weight: 600; color: #555; min-width: 50px; }
.rq-score { color: #888; }
.rq-feedback { color: #ff4d4f; font-style: italic; margin-left: auto; }

/* 自测组卷 */
.section-desc { color: #888; font-size: 14px; margin-bottom: 20px; line-height: 1.6; }
.difficulty-selector { display: flex; align-items: center; gap: 8px; }
.diff-btn {
  width: 36px; height: 36px; border-radius: 8px; border: 1.5px solid #e8e8e8; background: #fff;
  font-weight: 700; cursor: pointer; transition: all 0.15s; color: #666;
}
.diff-btn:hover { border-color: #91d5ff; }
.diff-btn.active { background: linear-gradient(135deg, #667eea, #764ba2); color: #fff; border-color: transparent; }
.diff-label { font-size: 13px; color: #999; margin-left: 4px; }

.loading-state { text-align: center; padding: 40px; color: #999; }
.loading-state p { margin-top: 12px; }

/* ========== 试卷编辑弹窗 v2 - 双栏布局 ========== */
.ep-layout { display: flex; gap: 0; height: 62vh; min-height: 420px; }

/* 左侧�?*/
.ep-sidebar {
  width: 280px; flex-shrink: 0;
  display: flex; flex-direction: column;
  background: #fafbfc; border-right: 1px solid #e8e8e8;
}
.ep-sidebar-header {
  padding: 14px 16px; border-bottom: 1px solid #e8e8e8;
  display: flex; flex-direction: column; gap: 8px;
}
.ep-name-input { font-size: 15px; font-weight: 600; color: #262626; }
.ep-total-score {
  font-size: 20px; font-weight: 800; color: #1890ff;
  text-align: right; letter-spacing: -0.5px;
}

.ep-question-list {
  flex: 1; overflow-y: auto; padding: 8px;
}
.ep-q-item {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 12px; margin-bottom: 4px;
  border-radius: 8px; cursor: pointer;
  border: 1.5px solid transparent;
  transition: all 0.15s ease;
}
.ep-q-item:hover { background: #fff; border-color: #d9d9d9; }
.ep-q-item.active {
  background: #fff; border-color: #1890ff;
  box-shadow: 0 2px 8px rgba(24,144,255,0.12);
}
.ep-q-badge {
  width: 28px; height: 28px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font-size: 13px; font-weight: 700; color: #fff;
  flex-shrink: 0;
}
.ep-q-badge.type-single_choice { background: linear-gradient(135deg, #667eea, #764ba2); }
.ep-q-badge.type-multiple_choice { background: linear-gradient(135deg, #f093fb, #f5576c); }
.ep-q-badge.type-true_false { background: linear-gradient(135deg, #4facfe, #00f2fe); }
.ep-q-badge.type-fill_blank { background: linear-gradient(135deg, #43e97b, #38f9d7); }
.ep-q-badge.type-subjective { background: linear-gradient(135deg, #fa709a, #fee140); }

.ep-q-info { flex: 1; min-width: 0; }
.ep-q-type { font-size: 11px; color: #999; text-transform: uppercase; letter-spacing: 0.5px; font-weight: 600; }
.ep-q-stem-preview {
  font-size: 13px; color: #555; white-space: nowrap; overflow: hidden;
  text-overflow: ellipsis; line-height: 1.4; margin-top: 2px;
}
.ep-q-score { font-size: 13px; font-weight: 700; color: #bbb; flex-shrink: 0; }
.ep-q-item.active .ep-q-score { color: #1890ff; }

.ep-add-btn { margin: 10px 14px; padding: 9px; font-size: 13px; border-radius: 8px; }

/* 右侧编辑�?*/
.ep-editor {
  flex: 1; display: flex; flex-direction: column;
  overflow: hidden; background: #fff;
}
.ep-editor-header {
  display: flex; align-items: center; justify-content: flex-start;
  padding: 16px 24px; border-bottom: 1px solid #f0f0f0;
  background: #fafbfc; gap: 32px;
}
.type-select-lg {
  padding: 8px 16px; border-radius: 8px; font-size: 14px; font-weight: 500;
  border-color: #d9d9d9; outline: none; background: #fff; color: #333;
  cursor: pointer; min-width: 120px;
}
.type-select-lg:focus { border-color: #1890ff; box-shadow: 0 0 0 3px rgba(24,144,255,0.08); }

.ep-score-wrap { display: flex; align-items: center; gap: 6px; font-size: 13px; color: #888; font-weight: 500; }
.score-input-lg { width: 64px !important; text-align: center; font-weight: 700; font-size: 16px; color: #1890ff; border-radius: 6px; }

.ep-editor-body {
  flex: 1; overflow-y: auto; padding: 24px 28px;
}
.ep-field { margin-bottom: 22px; }
.ep-field > label {
  display: block; font-size: 13px; font-weight: 600; color: #555;
  margin-bottom: 8px; letter-spacing: 0.3px;
}
.ep-hint { font-weight: 400; color: #bbb; font-size: 12px; margin-left: 4px; }

.ep-stem-area {
  resize: vertical; font-size: 15px; line-height: 1.7; padding: 14px 18px;
  border-radius: 8px; border-color: #d9d9d9; transition: border-color 0.2s;
  min-height: 120px;
}
.ep-stem-area:focus { border-color: #1890ff; box-shadow: 0 0 0 3px rgba(24,144,255,0.08); }

/* 选项网格 - 参考题库管理样�?*/
.ep-options-grid { display: flex; flex-direction: column; gap: 10px; }
.ep-opt-row {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 14px; background: #fafafa; border-radius: 8px;
  border: 1px solid #f0f0f0; transition: all 0.2s ease;
}
.ep-opt-row:hover { border-color: #d9d9d9; background: #fff; }
.ep-opt-key {
  font-size: 15px; font-weight: 700; color: #4b5563;
  width: 24px; text-align: center; flex-shrink: 0;
}
.ep-opt-content {
  flex: 1; font-size: 14px; border-radius: 6px; border-color: #d9d9d9;
}
.ep-opt-content:focus { border-color: #1890ff; box-shadow: 0 0 0 2px rgba(24,144,255,0.06); }

.ep-opt-radio, .opt-check {
  cursor: pointer; display: flex; align-items: center; justify-content: center;
  flex-shrink: 0; margin-left: auto;
}
.ep-opt-radio .ant-radio-wrapper, .opt-check .ant-checkbox-wrapper {
  margin-right: 0;
}

.ep-opt-del {
  width: 24px; height: 24px; border: none; background: #ff4d4f; color: white;
  cursor: pointer; font-size: 12px; border-radius: 50%; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  transition: all 0.2s ease; margin-left: 8px;
}
.ep-opt-del:hover:not(:disabled) { background: #ff7875; transform: scale(1.1); }
.ep-opt-del:disabled { opacity: 0.3; cursor: not-allowed; }

.ep-add-opt-btn {
  align-self: flex-start; padding: 8px 18px; font-size: 13px;
  border-radius: 6px; border-style: dashed; margin-top: 4px;
}

/* 判断题按钮组 */
.ep-tf-group { display: flex; gap: 12px; }
.ep-tf-btn {
  display: flex; align-items: center; gap: 8px;
  padding: 12px 28px; border-radius: 10px; border: 2px solid #e8e8e8;
  cursor: pointer; font-size: 15px; font-weight: 600; color: #888;
  transition: all 0.2s; user-select: none;
}
.ep-tf-btn:hover { border-color: #91d5ff; color: #555; }
.ep-tf-btn.active {
  border-color: #52c41a; background: #f6ffed; color: #389e0d;
}

.ep-answer-area {
  resize: vertical; font-size: 14px; line-height: 1.7; padding: 12px 16px;
  border-radius: 8px; border-color: #d9d9d9;
}
.ep-answer-area:focus { border-color: #1890ff; box-shadow: 0 0 0 3px rgba(24,144,255,0.08); }

/* 编辑器底部操作栏 - 已移至modal footer */
.ep-modal-footer {
  display: flex; justify-content: space-between; align-items: center;
  width: 100%; padding: 0;
}
.ep-order-btns { display: flex; gap: 8px; }
.ep-action-btns { display: flex; gap: 10px; }

/* 空状态提�?*/
.ep-empty-hint {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  color: #bbb; font-size: 15px; gap: 8px;
}
.ep-empty-hint .empty-icon { font-size: 40px; opacity: 0.5; }

/* ========== 查看试卷预览组件样式 ========== */
.preview-question-item {
  margin-top: 16px;
  padding: 20px;
  background-color: #fafbfc;
  border-radius: 12px;
  border: 1.5px solid #e8e8e8;
  transition: all 0.25s ease;
}
.preview-question-item:hover {
  border-color: #d9d9d9;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
}

.preview-q-header {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 12px;
  padding-bottom: 10px;
  border-bottom: 1px solid #f0f0f0;
}

.preview-q-stem {
  font-size: 15px;
  line-height: 1.7;
  color: #262626;
  margin-bottom: 14px;
  padding-left: 4px;
}

.preview-options-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-left: 8px;
}
.preview-options-list.preview-inline {
  flex-direction: row;
  gap: 16px;
}

.preview-option-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 14px;
  background: #fff;
  border: 1.5px solid #e8e8e8;
  border-radius: 8px;
  transition: all 0.2s ease;
  position: relative;
}
.preview-option-item:hover {
  border-color: #d9d9d9;
  background: #fafafa;
}

/* 正确答案高亮样式 */
.preview-option-item.correct-answer {
  background: linear-gradient(135deg, #f6ffed 0%, #ecfccb 100%);
  border-color: #52c41a;
  border-width: 2px;
  box-shadow: 0 2px 8px rgba(82, 196, 26, 0.15);
}
.preview-option-item.correct-answer::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 4px;
  background: linear-gradient(180deg, #52c41a, #389e0d);
  border-radius: 4px 0 0 4px;
}

.preview-opt-key {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: #f0f0f0;
  font-weight: 700;
  font-size: 13px;
  color: #666;
  flex-shrink: 0;
}
.preview-option-item.correct-answer .preview-opt-key {
  background: linear-gradient(135deg, #52c41a, #389e0d);
  color: #fff;
  box-shadow: 0 2px 4px rgba(82, 196, 26, 0.3);
}

.preview-opt-content {
  flex: 1;
  font-size: 14px;
  line-height: 1.5;
  color: #333;
}
.preview-option-item.correct-answer .preview-opt-content {
  color: #1a5c1a;
  font-weight: 500;
}

.correct-badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 3px 10px;
  background: linear-gradient(135deg, #52c41a, #389e0d);
  color: #fff;
  font-size: 12px;
  font-weight: 600;
  border-radius: 12px;
  white-space: nowrap;
  animation: correctBadgePulse 2s ease-in-out infinite;
}
@keyframes correctBadgePulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: 0.85; transform: scale(1.02); }
}

/* 填空/简答题参考答�?*/
.preview-answer-section {
  margin-top: 12px;
  margin-left: 8px;
  padding: 14px 16px;
  background: linear-gradient(135deg, #fffbe6 0%, #fff7e6 100%);
  border: 1.5px solid #ffd666;
  border-radius: 8px;
}

.preview-answer-label {
  display: block;
  font-size: 13px;
  font-weight: 600;
  color: #ad6800;
  margin-bottom: 8px;
}

.preview-answer-content {
  font-size: 14px;
  line-height: 1.7;
  color: #555;
  padding: 8px 12px;
  background: #fff;
  border-radius: 6px;
  border: 1px solid #ffe58f;
}

/* ========== 预览工具�?========== */
.preview-toolbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 20px;
  padding-bottom: 16px;
  border-bottom: 2px solid #f0f0f0;
}
.preview-title {
  margin: 0;
  font-size: 18px;
  font-weight: 700;
  color: #1a1a2e;
  display: flex;
  align-items: center;
  gap: 8px;
}
.preview-actions {
  display: flex;
  align-items: center;
  gap: 16px;
}
.preview-stats {
  font-size: 14px;
  color: #666;
  background: #f5f7fa;
  padding: 6px 14px;
  border-radius: 16px;
  font-weight: 500;
}
.preview-stats strong {
  color: #1890ff;
  margin: 0 4px;
}

/* 题目列表容器 */
.preview-questions-container {
  max-height: 600px;                    /* 限制最大高�?*/
  overflow-y: auto;                     /* 垂直滚动 */
  padding-right: 8px;
  scrollbar-width: thin;
  scrollbar-color: #d9d9d9 transparent;
}
.preview-questions-container::-webkit-scrollbar {
  width: 6px;
}
.preview-questions-container::-webkit-scrollbar-track {
  background: transparent;
}
.preview-questions-container::-webkit-scrollbar-thumb {
  background-color: #d9d9d9;
  border-radius: 3px;
}

/* 题目卡片 - 增强版（支持折叠�?/
.preview-question-item {
  margin-top: 12px;
  padding: 0;                           /* 移除内边距，由子元素控制 */
  background-color: #fafbfc;
  border-radius: 12px;
  border: 1.5px solid #e8e8e8;
  transition: all 0.25s ease;
  overflow: hidden;                    /* 包含展开/折叠动画 */
}
.preview-question-item:hover {
  border-color: #d9d9d9;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
}
.preview-question-item.expanded {
  border-color: #bae0ff;
  box-shadow: 0 4px 16px rgba(24, 144, 255, 0.08);
}

/* 题目头部（可点击�?/
.preview-q-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 10px;
  padding: 14px 18px;
  cursor: pointer;
  user-select: none;
  transition: background-color 0.2s ease;
  position: relative;
}
.preview-q-header:hover {
  background-color: #f0f5ff;
}
.preview-q-info {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}
.preview-q-score {
  color: #faad14;
  font-weight: 600;
  font-size: 13px;
}
.preview-toggle-icon {
  font-size: 12px;
  color: #999;
  transition: transform 0.3s ease;
  padding: 4px 8px;
  border-radius: 4px;
  background: #f0f0f0;
}
.preview-toggle-icon:hover {
  background: #e0e0e0;
  color: #666;
}

/* 折叠模式 - 摘要显示 */
.preview-q-summary {
  padding: 0 18px 14px 18px;
}
.preview-stem-truncated {
  margin: 0 0 6px 0;
  font-size: 14px;
  line-height: 1.6;
  color: #666;
  font-style: italic;
}
.preview-hint-text {
  display: inline-block;
  font-size: 12px;
  color: #1890ff;
  background: #e6f7ff;
  padding: 3px 10px;
  border-radius: 10px;
  cursor: pointer;
  transition: all 0.2s ease;
}
.preview-hint-text:hover {
  background: #bae0ff;
  color: #0958d9;
}

/* 展开模式 - 完整内容 */
.preview-q-full {
  padding: 0 18px 18px 18px;
  animation: slideDown 0.3s ease-out;
}
@keyframes slideDown {
  from {
    opacity: 0;
    transform: translateY(-10px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* 分页控件 */
.preview-pagination-wrapper {
  margin-top: 24px;
  padding-top: 20px;
  border-top: 2px solid #f0f0f0;
}
.preview-pagination {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 8px;
  margin-bottom: 12px;
  flex-wrap: wrap;
}
.btn-page-nav {
  padding: 8px 16px;
  font-size: 13px;
  font-weight: 600;
  border-radius: 8px;
  border: 2px solid #e8e8e8;
  background: #fff;
  color: #555;
  transition: all 0.25s ease;
}
.btn-page-nav:hover:not(:disabled) {
  border-color: #1890ff;
  color: #1890ff;
  transform: translateY(-1px);
  box-shadow: 0 2px 8px rgba(24, 144, 255, 0.15);
}
.btn-page-nav:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}
.preview-pages {
  display: flex;
  gap: 6px;
}
.btn-page-num {
  min-width: 36px;
  height: 36px;
  padding: 0 10px;
  font-size: 14px;
  font-weight: 600;
  border-radius: 8px;
  border: 2px solid #e8e8e8;
  background: #fff;
  color: #555;
  transition: all 0.2s ease;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.btn-page-num:hover:not(:disabled):not(.active):not(.ellipsis) {
  border-color: #91d5ff;
  color: #1890ff;
  background: #f0f9ff;
}
.btn-page-num.active {
  background: linear-gradient(135deg, #1890ff, #0958d9);
  border-color: #1890ff;
  color: #fff;
  box-shadow: 0 4px 12px rgba(24, 144, 255, 0.3);
  transform: scale(1.05);
}
.btn-page-num.ellipsis {
  border: none;
  background: transparent;
  cursor: default;
  color: #999;
}
.btn-page-num:disabled {
  cursor: not-allowed;
}
.preview-page-info {
  text-align: center;
  font-size: 13px;
  color: #888;
  padding: 8px;
  background: #fafafa;
  border-radius: 8px;
}
.preview-page-info strong {
  color: #1890ff;
}

/* ========== 发布试卷Modal ========== */
.publish-paper-modal .publish-modal-body {
  padding: 8px 0;
}
.publish-paper-modal .publish-paper-info {
  background: linear-gradient(135deg, #f0f9ff, #e6f7ff);
  border: 1px solid #bae7ff;
  border-radius: 8px;
  padding: 16px 20px;
  margin-bottom: 20px;
}
.publish-paper-modal .publish-paper-name {
  font-size: 18px;
  font-weight: 600;
  color: #1a1a2e;
  margin-bottom: 8px;
}
.publish-paper-modal .publish-paper-stats {
  display: flex;
  gap: 16px;
  color: #666;
  font-size: 14px;
}
.publish-paper-modal .publish-paper-stats span {
  display: flex;
  align-items: center;
  gap: 4px;
}
.publish-form-group {
  margin-bottom: 18px;
}
.publish-label {
  display: block;
  font-weight: 600;
  color: #333;
  margin-bottom: 8px;
  font-size: 14px;
}

.publish-mode-toggle {
  display: inline-flex;
  align-items: center;
  gap: 0;
  margin-left: 8px;
  border: 1px solid #d9d9d9;
  border-radius: 6px;
  overflow: hidden;
  vertical-align: middle;
  background: #fff;
}

.publish-mode-option {
  padding: 3px 14px;
  font-size: 12px;
  font-weight: 500;
  cursor: pointer;
  color: #666;
  transition: all 0.25s;
  user-select: none;
  line-height: 22px;
  background: #fff;
}

.publish-mode-option.active {
  background: #1890ff;
  color: #fff;
  font-weight: 500;
}

.publish-mode-option:not(.active):hover {
  color: #1890ff;
  background: #e6f7ff;
}

.publish-mode-option:first-child {
  border-right: 1px solid #d9d9d9;
}

.publish-mode-option.active + .publish-mode-option {
  border-left: none;
}

.publish-mode-hint {
  font-size: 12px;
  color: #999;
  margin-bottom: 8px;
  font-style: italic;
}
.publish-dept-display {
  padding: 8px 12px;
  background: #f5f5f5;
  border-radius: 6px;
  border: 1px solid #e8e8e8;
}
.publish-dept-display .dept-tag {
  color: #1890ff;
  font-weight: 500;
}
.publish-user-list {
  max-height: 200px;
  overflow-y: auto;
  border: 1px solid #e8e8e8;
  border-radius: 6px;
  padding: 8px;
}
.publish-user-empty {
  color: #999;
  text-align: center;
  padding: 16px;
  font-size: 13px;
}
.publish-user-loading {
  padding: 8px 12px;
}
.skeleton-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 0;
}
.skeleton-checkbox {
  width: 16px;
  height: 16px;
  border-radius: 3px;
  background: #e8e8e8;
  flex-shrink: 0;
  animation: skeleton-pulse 1.5s ease-in-out infinite;
}
.skeleton-text {
  height: 14px;
  border-radius: 4px;
  background: #e8e8e8;
  animation: skeleton-pulse 1.5s ease-in-out infinite;
}
@keyframes skeleton-pulse {
  0%, 100% { opacity: 0.4; }
  50% { opacity: 1; }
}
.publish-user-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 8px;
  border-radius: 4px;
  cursor: pointer;
  transition: background 0.2s;
}
.publish-user-item:hover {
  background: #f0f9ff;
}
.publish-user-item input[type="checkbox"] {
  accent-color: #1890ff;
}
.publish-user-dept {
  color: #999;
  font-size: 12px;
  margin-left: auto;
}

/* ========== 新用户选择组件样式 ========== */
.user-select-wrapper {
  border: 1px solid #e8e8e8;
  border-radius: 10px;
  overflow: hidden;
  background: #fff;
}

.user-select-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 12px 14px;
  background: #fafafa;
  border-bottom: 1px solid #e8e8e8;
  flex-wrap: wrap;
}

.user-search-box {
  display: flex;
  align-items: center;
  flex: 1;
  min-width: 200px;
  background: #fff;
  border: 1px solid #d9d9d9;
  border-radius: 6px;
  padding: 6px 10px;
  transition: all 0.25s;
}
.user-search-box:focus-within {
  border-color: #1890ff;
  box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.1);
}
.search-icon {
  font-size: 14px;
  opacity: 0.6;
  margin-right: 6px;
}
.user-search-input {
  flex: 1;
  border: none;
  outline: none;
  font-size: 13px;
  background: transparent;
}
.clear-search {
  cursor: pointer;
  opacity: 0.5;
  font-size: 12px;
  padding: 0 4px;
  transition: opacity 0.2s;
}
.clear-search:hover {
  opacity: 1;
}

.user-select-actions {
  display: flex;
  align-items: center;
  gap: 8px;
}
.btn-sm {
  padding: 4px 10px;
  font-size: 12px;
  border-radius: 5px;
}
.btn-danger {
  color: #ff4d4f;
  border-color: #ffa39e;
  background: #fff1f0;
}
.btn-danger:hover {
  background: #fff1f0 !important;
  color: #ff4d4f !important;
  border-color: #ff7875 !important;
}

.user-select-count {
  padding: 8px 14px;
  font-size: 12px;
  color: #666;
  background: #fafafa;
  border-bottom: 1px dashed #e8e8e8;
}
.selected-count {
  font-weight: 600;
  color: #1890ff;
  font-size: 14px;
}

.user-list-grid {
  max-height: 260px;
  overflow-y: auto;
  padding: 8px;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 8px;
}

.user-item-card {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  border: 1px solid #e8e8e8;
  border-radius: 8px;
  cursor: pointer;
  transition: all 0.25s ease;
  background: #fff;
}
.user-item-card:hover {
  border-color: #91caff;
  background: #f0f9ff;
  transform: translateY(-1px);
  box-shadow: 0 2px 8px rgba(24, 144, 255, 0.08);
}
.user-item-selected {
  border-color: #1890ff;
  background: #e6f7ff;
  box-shadow: 0 0 0 1px rgba(24, 144, 255, 0.2);
}

.user-checkbox {
  flex-shrink: 0;
}
.checkbox-box {
  width: 18px;
  height: 18px;
  border: 2px solid #d9d9d9;
  border-radius: 4px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 11px;
  color: #fff;
  font-weight: bold;
  transition: all 0.2s;
}
.user-item-selected .checkbox-box {
  background: #1890ff;
  border-color: #1890ff;
}

.user-info {
  flex: 1;
  min-width: 0;
}
.user-name {
  font-size: 14px;
  font-weight: 500;
  color: #262626;
  margin-bottom: 2px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.user-dept {
  font-size: 12px;
  color: #999;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.selected-users-section {
  padding: 12px 14px;
  background: #fafafa;
  border-top: 1px solid #e8e8e8;
}
.selected-users-header {
  margin-bottom: 8px;
}
.selected-users-title {
  font-size: 13px;
  font-weight: 600;
  color: #595959;
}
.selected-users-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.tag-dept {
  font-size: 11px;
  color: #999;
  margin-left: 2px;
}

/* ========== 用户分页样式 ========== */
.user-pagination-wrapper {
  padding: 12px 14px;
  background: #fafafa;
  border-top: 1px solid #e8e8e8;
}

.user-pagination {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  flex-wrap: wrap;
}

.page-btn,
.page-num {
  padding: 5px 12px;
  border: 1px solid #d9d9d9;
  border-radius: 5px;
  background: #fff;
  font-size: 13px;
  color: #595959;
  cursor: pointer;
  transition: all 0.2s;
  user-select: none;
}

.page-btn:hover:not(:disabled),
.page-num:hover:not(.active) {
  color: #1890ff;
  border-color: #1890ff;
  background: #e6f7ff;
}

.page-btn:disabled {
  cursor: not-allowed;
  opacity: 0.4;
}

.page-num.active {
  background: #1890ff;
  border-color: #1890ff;
  color: #fff;
}

.page-ellipsis {
  padding: 0 2px;
  color: #999;
  font-size: 14px;
  line-height: 1;
}

/* 加载状态优�?*/
.publish-user-loading {
  padding: 40px 20px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
}
.loading-text {
  font-size: 13px;
  color: #999;
}

.publish-summary {
  background: #fffbe6;
  border: 1px solid #ffe58f;
  border-radius: 6px;
  padding: 12px 16px;
}
.publish-summary-text {
  font-size: 13px;
  color: #ad8b00;
  line-height: 1.6;
}
</style>
