<template>
  <div class="exam-page">
    <!-- 功能标签栏 -->
    <TabsBar
      :tabs="examModuleTabs"
      v-model="activeTab"
    />

    <!-- ========== AI出题面板 ========== -->
    <div v-show="activeTab === 'generate' && canShowGenerate" class="exam-container">
      <!-- 顶部标题栏 -->
      <PageHeader
        title="AI智能出题系统"
        description="基于AI技术自动生成高质量试题"
      />

      <!-- 统计卡片 -->
      <StatsRow :stats="examTypeStats" />

      <!-- 出题表单卡片 -->
      <div class="exam-card">
        <div class="card-header">
          <h2>
            <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" style="vertical-align: -4px; margin-right: 6px;"><rect x="2" y="3" width="20" height="14" rx="2" ry="2"></rect><line x1="8" y1="21" x2="16" y2="21"></line><line x1="12" y1="17" x2="12" y2="21"></line></svg>
            智能出题配置
          </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"
            :show-drop-zone="false"
            placeholder="先选择知识库，再选择制度文件..."
          />
          <div v-if="selectedCollectionName" class="collection-info">
            <span class="collection-icon">
              <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"></path><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"></path></svg>
            </span>
            <span>当前知识库: <strong>{{ selectedCollectionName }}</strong></span>
          </div>
          <div v-if="selectedDocData" class="selected-file-info">
            <span class="file-info-icon">
              <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline></svg>
            </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">
            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" style="vertical-align: -3px; margin-right: 4px;"><path d="M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91-.09z"></path><path d="m12 15-3-3a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.35 22.35 0 0 1-4 2z"></path></svg>
            开始生成题目
          </button>
          <button class="btn btn-secondary" @click="generatedQuestions = []">
            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" style="vertical-align: -3px; margin-right: 4px;"><polyline points="3 6 5 6 21 6"></polyline><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path></svg>
            清空结果
          </button>
        </div>
      </div>

      <!-- 待审核题目列表（常驻展示） -->
      <div class="exam-card">
        <div class="card-header">
          <h2>
            <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" style="vertical-align: -4px; margin-right: 6px;"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline><line x1="16" y1="13" x2="8" y2="13"></line><line x1="16" y1="17" x2="8" y2="17"></line></svg>
            待审核题目列表
            <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)">
                  <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" style="vertical-align: -2px; margin-right: 2px;"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path></svg>
                  编辑
                </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)"
            >
              <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg>
              上一页
            </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)"
            >
              下一页
              <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg>
            </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' && canShowBank" class="exam-container">
      <div class="exam-header">
        <h1 class="exam-title">
          <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"></path><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"></path></svg>
          题库管理中心
        </h1>
        <div class="user-info"><span>共 {{ questions.length }} 道题目</span></div>
      </div>

      <div class="stats-row">
        <div class="stat-card">
          <div class="stat-icon">
            <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-3"></path><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path></svg>
          </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">
            <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path><polyline points="22 4 12 14.01 9 11.01"></polyline></svg>
          </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">
            <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="8" x2="12" y2="16"></line><line x1="8" y1="12" x2="16" y2="12"></line></svg>
          </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">
            <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-3"></path><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path></svg>
          </div>
            <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">
            <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="7"></rect><rect x="14" y="3" width="7" height="7"></rect><rect x="14" y="14" width="7" height="7"></rect><rect x="3" y="14" width="7" height="7"></rect></svg>
          </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>
            <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" style="vertical-align: -4px; margin-right: 6px;"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
            题目筛选
          </h2>
          <div class="header-actions">
            <button class="btn btn-primary btn-sm" @click="triggerImportFile">
              <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" style="vertical-align: -2px; margin-right: 4px;"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="7 10 12 15 17 10"></polyline><line x1="12" y1="15" x2="12" y2="3"></line></svg>
              批量导入
            </button>
            <button class="btn btn-secondary btn-sm" @click="handleBatchExport">
              <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" style="vertical-align: -2px; margin-right: 4px;"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="17 8 12 3 7 8"></polyline><line x1="12" y1="3" x2="12" y2="15"></line></svg>
              批量导出
            </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>
            <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" style="vertical-align: -4px; margin-right: 6px;"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline><line x1="16" y1="13" x2="8" y2="13"></line><line x1="16" y1="17" x2="8" y2="17"></line></svg>
            题目列表
          </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">
                    <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" style="vertical-align: -2px; margin-right: 2px;"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline></svg>
                    {{ 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">
                        <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path><polyline points="22 4 12 14.01 9 11.01"></polyline></svg>
                        参考答案：
                      </span>
                      <span class="bank-answer-content">{{ record.answer }}</span>
                    </div>
                    <div class="bank-answer-item" v-if="record.explanation">
                      <span class="bank-answer-label">
                        <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg>
                        解析：
                      </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--">
              <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg>
              上一页
            </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++">
              下一页
              <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg>
            </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' && canShowPaper" class="exam-container">
      <div class="exam-header">
        <h1 class="exam-title">
          <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline><line x1="16" y1="13" x2="8" y2="13"></line><line x1="16" y1="17" x2="8" y2="17"></line></svg>
          智能组卷系统
        </h1>
        <div class="user-info"><span>共 {{ papers.length }} 套试卷</span></div>
      </div>

      <div class="stats-row">
        <div class="stat-card">
          <div class="stat-icon">
            <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline><line x1="16" y1="13" x2="8" y2="13"></line><line x1="16" y1="17" x2="8" y2="17"></line></svg>
          </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">
            <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path><polyline points="22 4 12 14.01 9 11.01"></polyline></svg>
          </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">
            <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-3"></path><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path></svg>
          </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>
            <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" style="vertical-align: -4px; margin-right: 6px;"><rect x="2" y="3" width="20" height="14" rx="2" ry="2"></rect><line x1="8" y1="21" x2="16" y2="21"></line><line x1="12" y1="17" x2="12" y2="21"></line></svg>
            试卷配置
          </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">
            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" style="vertical-align: -3px; margin-right: 4px;"><path d="M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91-.09z"></path><path d="m12 15-3-3a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.35 22.35 0 0 1-4 2z"></path></svg>
            生成试卷
          </button>
          <button class="btn btn-success" @click="message.success('试卷模板保存成功！')">
            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" style="vertical-align: -3px; margin-right: 4px;"><path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"></path><polyline points="17 21 17 13 7 13 7 21"></polyline><polyline points="7 3 7 8 15 8"></polyline></svg>
            保存模板
          </button>
        </div>
      </div>

      <!-- 草稿箱（仅当有未发布的本地草稿时显示） -->
      <div v-if="localDraftPapers.length > 0" class="exam-card draft-card">
        <div class="card-header">
          <h2>
            <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" style="vertical-align: -4px; margin-right: 6px;"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-3"></path><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path></svg>
            草稿箱
          </h2>
          <span class="draft-hint">
            <a-tooltip title="草稿保存在浏览器本地，关闭标签页后会自动清除。如需长期保存，请点击「发布」按钮。">
              <span class="hint-icon">
                <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg>
              </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">
              <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline></svg>
            </div>
            <div class="paper-info">
              <div class="paper-name">{{ record.name }}</div>
              <div class="paper-meta">
                <span class="meta-tag">
                  <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline></svg>
                  {{ record.doc || '通用组卷' }}
                </span>
                <span class="meta-tag">
                  <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-3"></path><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path></svg>
                  {{ record.questions }} 题
                </span>
                <span class="meta-tag">
                  <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><polyline points="12 6 12 12 16 14"></polyline></svg>
                  {{ 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">
          <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"></path><line x1="12" y1="9" x2="12" y2="13"></line><line x1="12" y1="17" x2="12.01" y2="17"></line></svg>
          <span>草稿仅保存在当前浏览器会话中，关闭标签页后将自动清除。建议及时发布重要试卷。</span>
        </div>
      </div>

      <!-- 已发布试卷 -->
      <div class="exam-card">
        <div class="card-header" style="cursor: pointer;" @click="togglePublishedList">
          <h2>
            <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" style="vertical-align: -4px; margin-right: 6px;"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline><line x1="16" y1="13" x2="8" y2="13"></line><line x1="16" y1="17" x2="8" y2="17"></line></svg>
            已发布试卷
          </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">
              <svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round"><path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"></path><polyline points="22,6 12,13 2,6"></polyline></svg>
            </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">
                <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline><line x1="16" y1="13" x2="8" y2="13"></line><line x1="16" y1="17" x2="8" y2="17"></line></svg>
              </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">
                    <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-3"></path><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path></svg>
                    {{ record.questionCount || record.questions?.length || '?' }} 题
                  </span>
                  <span class="meta-tag">
                    <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="7"></rect><rect x="14" y="3" width="7" height="7"></rect><rect x="14" y="14" width="7" height="7"></rect><rect x="3" y="14" width="7" height="7"></rect></svg>
                    {{ formatScore(record.totalScore) }} 分
                  </span>
                  <span class="meta-tag status-success">
                    <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path><polyline points="22 4 12 14.01 9 11.01"></polyline></svg>
                    已发布
                  </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' && canShowTrain" class="exam-container">
      <!-- 训练标题栏 -->
      <div class="exam-header">
        <h1 class="exam-title">
          <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><circle cx="12" cy="12" r="6"></circle><circle cx="12" cy="12" r="2"></circle></svg>
          互动式训练系统
        </h1>
        <div class="user-info">
          <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path><circle cx="12" cy="7" r="4"></circle></svg>
          当前用户
        </div>
      </div>

      <!-- 训练统计 -->
      <div class="stats-row">
        <div class="stat-card">
          <div class="stat-icon">
            <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M6 9H4.5a2.5 2.5 0 0 1 0-5H6"></path><path d="M18 9h1.5a2.5 2.5 0 0 0 0-5H18"></path><path d="M4 22h16"></path><path d="M10 14.66V17c0 .55-.47.98-.97 1.21C7.85 18.75 7 20.24 7 22"></path><path d="M14 14.66V17c0 .55.47.98.97 1.21C16.15 18.75 17 20.24 17 22"></path><path d="M18 2H6v7a6 6 0 0 0 12 0V2Z"></path></svg>
          </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">
            <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect><line x1="16" y1="2" x2="16" y2="6"></line><line x1="8" y1="2" x2="8" y2="6"></line><line x1="3" y1="10" x2="21" y2="10"></line></svg>
          </div>
          <div class="stat-info">
            <div class="stat-value">{{ dailyExerciseCount }}</div>
            <div class="stat-label">今日练习</div>
          </div>
        </div>
      </div>

      <!-- 训练标签页 -->
      <div class="train-tabs">
        <button
          :class="['train-tab', { active: trainTab === 'checkpoint' }]"
          @click="trainTab = 'checkpoint'"
        >
          <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><polygon points="1 6 1 22 8 18 16 22 23 18 23 2 16 6 8 2 1 6"></polygon><line x1="8" y1="2" x2="8" y2="18"></line><line x1="16" y1="6" x2="16" y2="22"></line></svg>
          闯关模式
        </button>
        <button
          :class="['train-tab', { active: trainTab === 'daily' }]"
          @click="trainTab = 'daily'"
        >
          <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-3"></path><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path></svg>
          每日一练
        </button>
      </div>

      <!-- 闯关模式面板 -->
      <div v-if="trainTab === 'checkpoint'" class="exam-card">
        <div class="card-header">
          <h2>
            <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" style="vertical-align: -4px; margin-right: 6px;"><polygon points="1 6 1 22 8 18 16 22 23 18 23 2 16 6 8 2 1 6"></polygon><line x1="8" y1="2" x2="8" y2="18"></line><line x1="16" y1="6" x2="16" y2="22"></line></svg>
            闯关地图
          </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-show="activeTab === 'exam' && canShowExam" 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'">
          <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline><line x1="16" y1="13" x2="8" y2="13"></line><line x1="16" y1="17" x2="8" y2="17"></line></svg>
          待考试<span v-if="pendingPapers.length" class="tab-badge">{{ pendingPapers.length }}</span>
        </button>
        <button :class="['sub-tab', { active: examTab === 'taking' }]" @click="examTab = 'taking'" :disabled="!currentTakingPaper">
          <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path></svg>
          答题中
        </button>
        <button :class="['sub-tab', { active: examTab === 'results' }]" @click="loadExamResults()">
          <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="7"></rect><rect x="14" y="3" width="7" height="7"></rect><rect x="14" y="14" width="7" height="7"></rect><rect x="3" y="14" width="7" height="7"></rect></svg>
          成绩单<span v-if="completedResults.length" class="tab-badge">{{ completedResults.length }}</span>
        </button>
        <button :class="['sub-tab', { active: examTab === 'selftest' }]" @click="examTab = 'selftest'">
          <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"></circle><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path></svg>
          自测组卷
        </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 v-if="isStartingExam" class="starting-overlay">
          <div class="starting-content">
            <a-spin size="large" />
            <p class="starting-text">正在加载试卷题目...</p>
          </div>
        </div>
        <div :class="{ 'taking-blur': isStartingExam }">
        <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-header-info">
            <span class="taking-progress">第 {{ currentQuestionIndex + 1 }} / {{ currentTakingPaper.questionList?.length || 0 }} 题</span>
            <span class="answered-count">已答 {{ answeredQuestionCount }} 题</span>
          </div>
        </div>

        <div class="taking-layout">
          <!-- 侧边答题卡抽屉 -->
          <aside :class="['answer-drawer', { collapsed: answerCardCollapsed }]">
            <div class="drawer-toggle" @click="answerCardCollapsed = !answerCardCollapsed" :title="answerCardCollapsed ? '展开答题卡' : '收起答题卡'">
              <span class="toggle-icon">{{ answerCardCollapsed ? '☰' : '✕' }}</span>
              <span v-if="answerCardCollapsed" class="toggle-label">答题卡</span>
            </div>

            <div class="drawer-content" v-show="!answerCardCollapsed">
              <div class="answer-card-header">
                <h3>☐ 答题卡</h3>
              </div>

              <div class="answer-grid" ref="answerGridRef">
                <button
                  v-for="(q, idx) in currentTakingPaper.questionList"
                  :key="q.questionId"
                  :class="['answer-cell', {
                    'is-current': idx === currentQuestionIndex,
                    'is-answered': !!takingAnswers[q.questionId],
                    'is-unanswered': !takingAnswers[q.questionId]
                  }]"
                  @click="goToQuestion(idx)"
                  :title="getAnswerCellTitle(q, idx)">
                  <span class="cell-num">{{ idx + 1 }}</span>
                  <span v-if="!!takingAnswers[q.questionId]" class="cell-check">✓</span>
                </button>
              </div>

              <div class="answer-legend">
                <div class="legend-item"><span class="legend-dot current"></span>当前</div>
                <div class="legend-item"><span class="legend-dot answered"></span>已答</div>
                <div class="legend-item"><span class="legend-dot unanswered"></span>未答</div>
              </div>
            </div>
          </aside>

          <!-- 右侧题目区域 -->
          <main class="taking-main">
            <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="prevQuestion()">
                ← 上一题
              </button>
              <button v-if="currentTakingPaper.questionList && currentQuestionIndex < currentTakingPaper.questionList.length - 1" class="btn btn-primary" @click="nextQuestion()">
                下一题 →
              </button>
              <button v-else class="btn btn-success" @click="submitExamAnswers()" :disabled="isSubmitting">
                {{ isSubmitting ? '提交中...' : '↑ 提交试卷' }}
              </button>
            </div>
          </main>
        </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>
          <a-button type="primary" size="small" @click="loadExamResults()" :loading="isLoadingResults">刷新</a-button>
        </div>
        <div v-if="isLoadingResults" class="loading-state"><a-spin /><p>加载中...</p></div>
        <div v-else-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', isQuestionCorrect(qr) ? 'correct' : 'wrong']">
                <span class="rq-status">{{ isQuestionCorrect(qr) ? '✅' : '❌' }}</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="!isQuestionCorrect(qr) && getQuestionFeedback(qr)">{{ getQuestionFeedback(qr) }}</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 === '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;">
          <svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path></svg>
        </div>
        <h3 style="margin-bottom: 16px;">正在导入题目...</h3>
        <p style="color: var(--text-tertiary);">请稍候，正在解析题目文件</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: var(--text-secondary); 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: var(--text-tertiary);">
            暂无题目预览
          </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: var(--color-primary);">{{ currentCheckpoint.name }}</h3>

        <div v-for="(q, index) in currentCheckpointQuestions" :key="q.id" style="margin-bottom: 20px; padding: 16px; background: var(--bg-container); border-radius: 8px; border: 1px solid var(--border-light);">
          <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: var(--text-tertiary);">
        正在加载题目...
      </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: isQuestionCorrect(item) === true, wrong: isQuestionCorrect(item) === 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="isQuestionCorrect(item) === true" class="grade-item-tag correct">✓ 正确</span>
              <span v-else-if="isQuestionCorrect(item) === false" class="grade-item-tag wrong">✗ 错误</span>
              <span v-else class="grade-item-tag subjective">主观题</span>
            </div>
            <div v-if="getQuestionFeedback(item)" class="grade-item-feedback">{{ getQuestionFeedback(item) }}</div>
            <div v-if="item.details?.scoring_breakdown && !item.details?.overall_feedback" class="grade-item-detail-feedback">
              <strong>评分明细：</strong>
              <div class="scoring-breakdown">
                <div v-for="(score, key) in item.details.scoring_breakdown" :key="key" class="scoring-item">
                  {{ key }}: {{ score }}
                </div>
              </div>
            </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>
</template>

<script setup>
import { ref, reactive, computed, watch, onMounted, onBeforeMount, 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, usePermissions } from '../utils/permission.js'
import FileSelector from './FileSelector.vue'
import TabsBar from './common/TabsBar.vue'
import PageHeader from './common/PageHeader.vue'
import StatsRow from './common/StatsRow.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'
import { navigateToExamWithFileEvent } from '../utils/eventBus.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 answerCardCollapsed = ref(false)
const answerGridRef = ref(null)

const answeredQuestionCount = computed(() => {
  if (!currentTakingPaper.value?.questionList) return 0
  return currentTakingPaper.value.questionList.filter(q => {
    const ans = takingAnswers.value[q.questionId]
    return ans !== undefined && ans !== null && ans !== '' && (Array.isArray(ans) ? ans.length > 0 : true)
  }).length
})

const goToQuestion = (idx) => {
  if (idx >= 0 && idx < (currentTakingPaper.value?.questionList?.length || 0)) {
    currentQuestionIndex.value = idx
    nextTick(() => {
      const grid = answerGridRef.value
      if (grid) {
        const cell = grid.children[idx]
        if (cell) cell.scrollIntoView({ behavior: 'smooth', block: 'nearest' })
      }
    })
  }
}

const prevQuestion = () => {
  if (currentQuestionIndex.value > 0) currentQuestionIndex.value--
}

const nextQuestion = () => {
  if (currentTakingPaper.value?.questionList && currentQuestionIndex.value < currentTakingPaper.value.questionList.length - 1) {
    currentQuestionIndex.value++
  }
}

const getAnswerCellTitle = (q, idx) => {
  const ans = takingAnswers.value[q.questionId]
  if (ans !== undefined && ans !== null && ans !== '') {
    let preview = ''
    if (Array.isArray(ans)) preview = ans.join(',')
    else if (ans === 'true') preview = '正确'
    else if (ans === 'false') preview = '错误'
    else preview = String(ans).substring(0, 20)
    return `第${idx + 1}题（已答：${preview}）`
  }
  return `第${idx + 1}题（未作答）`
}

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 isLoadingPapers = ref(false)
const isLoadingResults = ref(false)
const isStartingExam = 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 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, options = {}) => {
  if (questionPagination.loading) return

  questionPagination.loading = true
  questionPagination.current = page

  const { skipStatusFilter = false, includeAllStatuses = false } = options

  console.log(`\n📚 [${new Date().toLocaleTimeString()}] 开始加载第 ${page} 页题目...`)
  console.log(`📚 调用选项: skipStatusFilter=${skipStatusFilter}, includeAllStatuses=${includeAllStatuses}`)

  try {
    const params = {
      pageNum: page,
      pageSize: questionPagination.pageSize
    }

    if (!skipStatusFilter && !includeAllStatuses) {
      if (pendingListStatus.value && pendingListStatus.value !== 'all') {
        params.status = pendingListStatus.value
      }
    } else if (includeAllStatuses) {
      console.log('📚 查询所有状态的题目（不限制status）')
    }

    console.log('📚 最终查询参数:', params)

    const res = await questionAPI.pageQuestions(params)

    console.log('📚 分页查询原始响应:', res)
    console.log('📚 响应结构:', {
      hasRes: !!res,
      resKeys: res ? Object.keys(res) : [],
      hasData: !!(res?.data),
      dataCode: res?.data?.code,
      hasDataData: !!(res?.data?.data),
      dataType: typeof res?.data?.data
    })

    let questionsList = []
    let total = 0

    if (res?.data?.code === 200 && res.data.data) {
      console.log('✅ 响应格式正确（code=200）')

      if (res.data.data.records) {
        questionsList = res.data.data.records
        total = res.data.data.total || questionsList.length
        console.log(`   - 使用 records 字段: ${questionsList.length} 条, total=${total}`)

        if (questionsList.length > 0) {
          const statusCount = {}
          questionsList.forEach(q => {
            const s = q.status || 'unknown'
            statusCount[s] = (statusCount[s] || 0) + 1
          })
          console.log(`   - 题目状态分布:`, statusCount)
        }
      } else if (Array.isArray(res.data.data)) {
        questionsList = res.data.data
        total = questionsList.length
        console.log(`   - 使用数组格式: ${questionsList.length} 条`)
      } else if (res.data.data.list) {
        questionsList = res.data.data.list
        total = res.data.data.total || questionsList.length
        console.log(`   - 使用 list 字段: ${questionsList.length} 条, total=${total}`)
      } else {
        console.warn('   ⚠️ 未找到预期的数据格式:', res.data.data)
      }
    } else {
      console.error('❌ 响应格式异常:', {
        code: res?.data?.code,
        message: res?.data?.message,
        data: res?.data?.data
      })
    }

    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}页加载完成:`)
    console.log(`   - 题目数量: ${questionsList.length}`)
    console.log(`   - 总计: ${total}`)
    console.log(`   - generatedQuestions.length: ${generatedQuestions.value.length}`)

  } catch (error) {
    console.error('❌ 分页查询题目失败:', error)
    console.error('   错误详情:', error.message)
    console.error('   错误堆栈:', error.stack)
    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)
  }
}

// ✅ 终极修复：直接从 localStorage 读取权限，绕过所有 Vue 响应式问题
const getLocalPermission = (code) => {
  try {
    const stored = JSON.parse(localStorage.getItem('userPermissions'))
    if (Array.isArray(stored)) {
      return stored.some(p => {
        if (typeof p === 'string') return p === code
        return p?.permissionCode === code || p?.permission_code === code
      })
    }
  } catch (e) {}
  return false
}

// 权限控制 computed - 直接读取 localStorage
const canShowGenerate = computed(() => getLocalPermission('exam:ai'))
const canShowBank = computed(() => getLocalPermission('exam:bank'))
const canShowPaper = computed(() => getLocalPermission('exam:paper'))
const canShowTrain = computed(() => {
  const result = getLocalPermission('exam:train')
  return result
})
const canShowExam = computed(() => getLocalPermission('exam:exam'))

const examModuleTabs = computed(() => {
  const tabs = []
  if (canShowGenerate.value) {
    tabs.push({ key: 'generate', label: 'AI出题', icon: 'robot' })
  }
  if (canShowBank.value) {
    tabs.push({ key: 'bank', label: '题库管理', icon: 'book' })
  }
  if (canShowPaper.value) {
    tabs.push({ key: 'paper', label: '智能组卷', icon: 'clipboard' })
  }
  if (canShowTrain.value) {
    tabs.push({ key: 'train', label: '互动训练', icon: 'target' })
  }
  if (canShowExam.value) {
    tabs.push({ key: 'exam', label: '试卷考核', icon: 'edit' })
  }
  console.log('📋 [ExamModule] 生成的标签页列表:', tabs.map(t => t.key).join(', '))
  return tabs
})

// 获取第一个有权限的标签页
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()
})

watch(navigateToExamWithFileEvent, (payload) => {
  if (!payload) return

  // 检查是否有 AI 出题权限，如果没有则切换到第一个有权限的 tab
  const targetTab = canShowGenerate.value ? 'generate' : getFirstAccessibleTab()
  if (targetTab) {
    activeTab.value = targetTab
    if (!canShowGenerate.value && targetTab !== 'generate') {
      console.warn('⚠️ [ExamModule] 用户无 AI 出题权限，已切换到:', targetTab)
      message.warning('您没有 AI 出题权限，已为您切换到其他功能')
    }
  }
  
  selectedDoc.value = payload.docId || payload.fileData?.id
  selectedDocData.value = {
    id: payload.fileData?.id,
    title: payload.fileData?.title || payload.fileData?.fileName || payload.docName,
    rawFileName: payload.fileData?.fileName || payload.docName,
    version: payload.fileData?.currentVersion || 1
  }
  selectedCollectionName.value = payload.collectionName || ''
  
  nextTick(() => {
    if (onDocSelected) {
      onDocSelected(selectedDocData.value)
    }
    
    message.success({
      content: `已自动选择文件：${selectedDocData.value.title}`,
      duration: 2.5
    })
  })
}, { deep: true })

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 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 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
})

// 计算属性：是否启用分页（超过10题时自动启用）
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 examTypeStats = computed(() => [
  { value: typeStats.value.single, label: '单选题' },
  { value: typeStats.value.multiple, label: '多选题' },
  { value: typeStats.value.judgment, label: '判断题' },
  { value: typeStats.value.essay, label: '简答题' }
])

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: [],
})

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)
let generatePollTimer = null
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 doc = selectedDocData.value
    let fileId = doc?.id || doc?.fileId
    // 向量库的文件：真正的 file_id 可能嵌套在 sourceData 中
    if (!fileId && doc?.sourceData) {
      fileId = doc.sourceData.metadata?.file_id || doc.sourceData.file_id || doc.sourceData.id
    }

    console.log('📋 出题参数详情:')
    console.log('  - selectedDocData:', JSON.stringify(doc, null, 2))
    console.log('  - fileId:', fileId)
    console.log('  - questionTypes:', JSON.stringify(activeTypes))

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

    if (fileId) {
      params.file_id = Number(fileId)
    } else if (doc?.pdfUrl || doc?.filePath || doc?.path) {
      params.file_path = doc.pdfUrl || doc.filePath || doc.path
      params.collection = selectedCollectionName.value || 'public_kb'
    } else {
      message.error('无法获取文件信息，请重新选择文件后重试')
      return
    }

    generateProgress.value = 40

    console.log('🚀 开始调用生成题目API，参数:', JSON.stringify(params, null, 2))

    const result = await examAPI.generateQuestions(params)

    console.log('📥 题目生成API返回结果:', result)
    console.log('📥 结果结构:', {
      hasResult: !!result,
      requestId: result?.requestId || result?.request_id,
      message: result?.message,
      dataFileId: result?.data?.fileId,
      success: result?.success,
      total: result?.total,
      allKeys: result ? Object.keys(result) : []
    })

    generateProgress.value = 90

    if (result && (result.requestId || result.request_id || result.message)) {
      generateProgress.value = 90
      console.log('✅ 题目生成任务已成功提交')
      console.log(`   - RequestId: ${result.requestId || result.request_id}`)
      console.log(`   - Message: ${result.message}`)
      console.log('⏳ 开始轮询等待异步生成完成...')

      questionPagination.current = 1
      questionPagination.total = 0

      const startTime = new Date()
      console.log(`📅 记录开始时间: ${startTime.toLocaleTimeString()}`)

      const fileId = selectedDocData.value?.id
      const filePathForCheck = selectedDocData.value?.path || selectedDocData.value?.filePath

      let baseTotal = 0
      try {
        await loadGeneratedQuestions(1, { includeAllStatuses: true })
        baseTotal = questionPagination.total
        console.log(`📊 生成前的题目总数: ${baseTotal}`)
        console.log('⏳ 等待RAG服务异步生成新题目...')
        console.log(`📁 文件ID: ${fileId}, 路径: ${filePathForCheck}`)
      } catch (e) {
        console.warn('⚠️ 获取基准题目数失败:', e)
        baseTotal = 0
      }

      const maxPollAttempts = 40
      const initialPollIntervalMs = 2000
      const maxPollIntervalMs = 10000
      let currentPollIntervalMs = initialPollIntervalMs
      let pollCount = 0
      let found = false
      let newQuestionsCount = 0
      let lastFileStatus = null
      let totalElapsedTime = 0

      console.log(`⚙️ 轮询配置:`)
      console.log(`   - 最大次数: ${maxPollAttempts}`)
      console.log(`   - 初始间隔: ${initialPollIntervalMs/1000}秒`)
      console.log(`   - 最大间隔: ${maxPollIntervalMs/1000}秒`)
      console.log(`   - 退避策略: 每3次翻倍`)

      while (pollCount < maxPollAttempts && !found) {
        pollCount++
        generateProgress.value = Math.min(95, 90 + (pollCount / maxPollAttempts) * 5)

        console.log(`\n${'='.repeat(60)}`)
        console.log(`🔄 [${new Date().toLocaleTimeString()}] 第 ${pollCount}/${maxPollAttempts} 次轮询`)
        console.log(`   ⏱️ 当前间隔: ${(currentPollIntervalMs/1000).toFixed(1)}秒 | 总耗时: ${totalElapsedTime.toFixed(0)}秒`)

        try {
          if (fileId) {
            console.log(`   🔍 检查文件状态 (FileID: ${fileId})...`)
            try {
              const fileRes = await fileAPI.getFileInfo(fileId)
              if (fileRes?.data?.data) {
                const fileInfo = fileRes.data.data
                const prevStatus = lastFileStatus?.examStatus
                lastFileStatus = {
                  examStatus: fileInfo.examStatus,
                  processMessage: fileInfo.processMessage
                }

                console.log(`   📁 文件状态更新:`)
                console.log(`      - examStatus: ${fileInfo.examStatus || '未设置'} ${prevStatus && prevStatus !== fileInfo.examStatus ? `(从 ${prevStatus} 变化)` : ''}`)
                console.log(`      - processMessage: ${fileInfo.processMessage || '无'}`)

                if (fileInfo.examStatus === 'FAILED') {
                  console.error(`\n❌❌❌ RAG服务生成失败！❌❌❌`)
                  console.error(`错误信息: ${fileInfo.processMessage}`)
                  message.error({
                    content: `RAG服务生成失败: ${fileInfo.processMessage || '未知错误'}`,
                    duration: 0
                  })
                  generateError.value = `RAG服务生成失败: ${fileInfo.processMessage}`
                  break
                } else if (fileInfo.examStatus === 'GENERATED' || fileInfo.examStatus === 'COMPLETED') {
                  console.log(`\n✅✅✅ 文件状态已变为"${fileInfo.examStatus}"！应该有新题目了 ✅✅✅`)
                } else if (fileInfo.examStatus === 'GENERATING') {
                  console.log(`   ⏳ RAG服务正在生成中...`)
                }
              }
            } catch (fileErr) {
              console.warn(`   ⚠️ 获取文件状态失败:`, fileErr.message)
            }
          }

          await loadGeneratedQuestions(1, { includeAllStatuses: true })

          const currentTotal = questionPagination.total
          newQuestionsCount = Math.max(0, currentTotal - baseTotal)

          console.log(`   📊 题目统计:`)
          console.log(`      - 当前总数: ${currentTotal}`)
          console.log(`      - 基准总数: ${baseTotal}`)
          console.log(`      - 新增数量: ${newQuestionsCount}`)

          if (newQuestionsCount > 0) {
            found = true
            console.log(`\n🎉🎉🎉 成功检测到 ${newQuestionsCount} 道新题目！🎉🎉🎉`)
            console.log(`总计: ${currentTotal} 道 | 耗时: ${(totalElapsedTime/1000).toFixed(1)}秒`)
            if (lastFileStatus) {
              console.log(`📁 最终文件状态: ${lastFileStatus.examStatus}`)
            }
          } else {
            if (pollCount < 10) {
              console.log(`   💡 提示: 正在等待RAG服务生成...`)
              if (baseTotal > 0) {
                console.log(`   ℹ️ 数据库中已有${baseTotal}道历史题目`)
              }
            } else if (pollCount % 5 === 0) {
              console.log(`   ⏳ 已轮询${pollCount}次，继续耐心等待...`)
            }
          }
        } catch (e) {
          console.error(`   ❌ 查询失败:`, e.message)
        }

        if (!found && pollCount < maxPollAttempts) {
          if (pollCount % 3 === 0 && currentPollIntervalMs < maxPollIntervalMs) {
            currentPollIntervalMs = Math.min(currentPollIntervalMs * 2, maxPollIntervalMs)
            console.log(`   📈 调整轮询间隔: ${(currentPollIntervalMs/1000).toFixed(1)}秒 (指数退避)`)
          }

          console.log(`   ⏸️ 等待 ${(currentPollIntervalMs/1000).toFixed(1)}秒...`)
          await new Promise(resolve => setTimeout(resolve, currentPollIntervalMs))
          totalElapsedTime += currentPollIntervalMs
        }
      }

      generateProgress.value = 100

      const finalTimeSec = (totalElapsedTime / 1000).toFixed(1)

      if (found && newQuestionsCount > 0) {
        message.success(`✅ 题目生成成功！共新增 ${newQuestionsCount} 道题目（总计: ${questionPagination.total} 道）`)
        console.log(`\n${'='.repeat(60)}`)
        console.log(`🎉 最终结果: 成功生成 ${newQuestionsCount} 道新题目`)
        console.log(`${'='.repeat(60)}`)
        console.log(`📊 统计信息:`)
        console.log(`   - 新增题目: ${newQuestionsCount} 道`)
        console.log(`   - 总计题目: ${questionPagination.total} 道`)
        console.log(`   - 总耗时: ${finalTimeSec} 秒`)
        console.log(`   - 轮询次数: ${pollCount} 次`)
        if (lastFileStatus) {
          console.log(`   - 文件状态: ${lastFileStatus.examStatus}`)
          if (lastFileStatus.processMessage) {
            console.log(`   - 状态信息: ${lastFileStatus.processMessage}`)
          }
        }
        console.log(`📋 前5道题目详情:`)
        generatedQuestions.value.slice(0, 5).forEach((q, idx) => {
          console.log(`   [${idx + 1}] ${q.typeLabel} | 状态: ${q.status} | 难度: ${q.difficulty}`)
        })
        if (generatedQuestions.value.length > 5) {
          console.log(`   ... 还有 ${generatedQuestions.value.length - 5} 道`)
        }
        console.log(`${'='.repeat(60)}\n`)
      } else if (found && newQuestionsCount === 0) {
        console.warn(`⚠️ 异常：found=true 但 newQuestionsCount=0`)
        message.warning(`⚠️ 题目生成异常：未检测到新增题目（总计: ${questionPagination.total} 道）`)
        console.error(`❌ 诊断结论: RAG服务未成功生成并保存新题目到数据库`)
        console.error(`   请检查以下内容:`)
        console.error(`   1. 后端日志：搜索 "异步生成题目失败" 或 "调用RAG服务生成题目失败"`)
        console.error(`   2. RAG服务状态：确认AI模型服务是否正常运行`)
        console.error(`   3. 文件路径：确认 ${filePathForCheck} 在知识库中已同步`)
        generateError.value = `RAG服务未能成功生成题目。请检查后端控制台日志查看详细错误信息。`
      } else {
        console.warn(`\n${'='.repeat(60)}`)
        console.warn(`⚠️ 轮询超时：已等待 ${finalTimeSec} 秒，仍未找到新题目`)
        console.warn(`${'='.repeat(60)}`)
        console.warn(`📋 完整诊断报告:`)
        console.warn(``)
        console.warn(`📁 文件信息:`)
        console.warn(`   - FileID: ${fileId}`)
        console.warn(`   - FilePath: ${filePathForCheck}`)
        console.warn(`   - Collection: ${selectedDocData.value?.collectionName}`)
        if (lastFileStatus) {
          console.warn(`   - 最终状态: ${lastFileStatus.examStatus}`)
          console.warn(`   - 状态信息: ${lastFileStatus.processMessage || '无'}`)
        }
        console.warn(``)
        console.warn(`📊 题目统计:`)
        console.warn(`   - 基准总数: ${baseTotal}`)
        console.warn(`   - 最终总数: ${questionPagination.total}`)
        console.warn(`   - 新增数量: 0 (这是问题所在)`)
        console.warn(``)
        console.warn(`⏱️ 时间统计:`)
        console.warn(`   - 开始时间: ${startTime.toLocaleTimeString()}`)
        console.warn(`   - 结束时间: ${new Date().toLocaleTimeString()}`)
        console.warn(`   - 总耗时: ${finalTimeSec} 秒`)
        console.warn(`   - 轮询次数: ${pollCount}/${maxPollAttempts}`)
        console.warn(`   - 最终间隔: ${(currentPollIntervalMs/1000).toFixed(1)}秒`)
        console.warn(``)
        console.warn(`🔍 根本原因分析:`)
        console.warn(`   ✅ 前端API调用正常（已收到requestId）`)
        console.warn(`   ✅ 后端接收请求正常（已启动异步任务）`)
        if (lastFileStatus?.examStatus === 'GENERATING') {
          console.warn(`   ⚠️ RAG服务一直在"生成中"状态（可能卡住或超时）`)
        } else if (lastFileStatus?.examStatus === 'FAILED') {
          console.warn(`   ❌ RAG服务已标记为失败（但未被及时检测到）`)
        } else if (!lastFileStatus?.examStatus) {
          console.warn(`   ⚠️ 文件状态未更新（异步任务可能未执行）`)
        } else {
          console.warn(`   ❌ RAG服务调用失败或返回空数据`)
        }
        console.warn(`   ❌ 没有新题目被保存到数据库`)
        console.warn(``)
        console.warn(`💡 解决方案:`)
        console.warn(`   1. 立即检查后端控制台日志`)
        console.warn(`   2. 搜索关键词: "generateQuestions" 或 "RAG服务" 或 "ERROR"`)
        console.warn(`   3. 确认RAG/AI服务是否正常启动`)
        console.warn(`   4. 确认文件已在向量库中完成向量化`)
        console.warn(`${'='.repeat(60)}\n`)

        message.error({
          content: `❌ 题目生成失败：RAG服务未能在 ${finalTimeSec} 秒内生成题目`,
          duration: 0
        })
        generateError.value = `已等待${finalTimeSec}秒，RAG服务未返回任何新题目。\n\n可能的原因：\n1. RAG/AI服务未启动或响应超时\n2. 文件未在向量库中完成同步\n3. AI模型调用失败\n\n请检查后端日志获取详细信息。`
        generatedQuestions.value = []
      }
    } else if (result && (result.error || result.errorCode || result.code && result.code !== 200)) {
      const errorMsg = result.error || result.errorCode || result.message || 'RAG服务生成题目失败'
      console.error('❌ RAG服务返回失败:', { error: result.error, errorCode: result.errorCode, requestId: result.requestId })
      generateError.value = `题目生成失败: ${errorMsg}`
    } 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
}

// 生成页码数组（最多显示9个页码）
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) {
      // 前5页：显示1-7 + ... + 尾页
      for (let i = 1; i <= 7; i++) pages.push(i)
      pages.push('...')
      pages.push(total)
    } else if (current >= total - 3) {
      // 后5页：显示1 + ... + 最后7页
      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 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
    }

    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 isQuestionCorrect = (resultItem) => {
  return resultItem.is_correct ?? resultItem.correct
}

const getQuestionFeedback = (resultItem) => {
  if (resultItem.feedback) return resultItem.feedback
  if (!resultItem.details) return ''
  
  const { details } = resultItem
  
  if (details.overall_feedback) return details.overall_feedback
  
  if (details.blank_scores && details.correct_answers) {
    const parts = []
    const blankScores = details.blank_scores
    const correctAnswers = details.correct_answers
    
    Object.keys(blankScores).forEach((key, idx) => {
      const score = blankScores[key]
      const correctAnswer = correctAnswers[key]
      if (score > 0) {
        parts.push(`第${idx + 1}空正确`)
      } else {
        parts.push(`第${idx + 1}空答案应为：${correctAnswer}`)
      }
    })
    
    return parts.join('，')
  }
  
  if (details.scoring_breakdown) {
    return Object.entries(details.scoring_breakdown)
      .map(([key, val]) => `${key}: ${val}`)
      .join('; ')
  }
  
  return ''
}

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}`)
  } catch (error) {
    console.error('批改失败:', error)
    gradeResult.value = null
    message.error('批改失败: ' + (error.message || '请检查网络连接'))
  } finally {
    isGrading.value = false
  }
}

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 = {}
  isStartingExam.value = true
  examTab.value = 'taking'

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

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

  try {
    if (!paper.questionList?.length && paperId) {
      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)
  } finally {
    isStartingExam.value = false
  }

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

  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 paperId = paper.paperId || paper.paper_id || ''
        const formattedAnswers = answers.map(a => {
          const raw = a.student_answer
          const qType = a.question_type
          let answer
          if (qType === 'true_false') {
            if (typeof raw === 'boolean') answer = raw ? 'true' : 'false'
            else if (typeof raw === 'string') answer = (raw.trim().toUpperCase() === 'T' || raw.trim().toUpperCase() === 'TRUE' || raw.trim() === '正确') ? 'true' : 'false'
            else answer = 'false'
          } else if (qType === 'multiple_choice' || qType === 'fill_blank') {
            answer = Array.isArray(raw) ? raw.filter(v => v != null && v !== '') : (raw ? [raw] : [])
          } else {
            answer = raw != null ? String(raw).trim() : ''
          }
          return { questionId: a.question_id, questionType: qType, answer }
        })

        resultData = await examAPI.submitFormalExam({ recordId: currentRecordId.value, answers: formattedAnswers })
        console.log('[试卷考核] 正式考试批改完成:', resultData)
      } catch (gradeErr) {
        console.warn('[试卷考核] 正式考试接口失败，重试:', gradeErr.message)

        const retryAnswers = formattedAnswers
        resultData = await examAPI.submitFormalExam({ recordId: currentRecordId.value, answers: retryAnswers })
      }
    } 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 loadExamResults = async () => {
  examTab.value = 'results'
  isLoadingResults.value = true
  try {
    const records = await examAPI.getExamRecords('formal_exam')
    if (Array.isArray(records)) {
      completedResults.value = records
        .filter(r => r.status === 'completed' || r.status === 'graded')
        .map(r => ({
          recordId: r.recordId || r.id,
          paperName: r.paperTitle || r.paperName || '试卷',
          totalScore: r.totalScore != null ? Number(r.totalScore) : (r.score != null ? Number(r.score) : 0),
          totalMaxScore: r.totalMaxScore || r.total_max_score || 100,
          scoreRate: r.scoreRate != null ? Number(r.scoreRate) : (r.totalScore && r.totalMaxScore ? Number(r.totalScore) / Number(r.totalMaxScore) : 0),
          completedAt: r.submitTime || r.completedAt || r.submittedAt || r.updatedAt,
          results: null
        }))
    }
  } catch (error) {
    console.error('[成绩单] 加载失败:', error)
  } finally {
    isLoadingResults.value = false
  }
}

const viewExamDetail = async (result) => {
}

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: var(--space-lg);
  overflow-y: auto;
  overflow-x: hidden;
  min-width: 0;
}

/* 性能友好降级 */
@media (prefers-reduced-motion: reduce) {
  * {
    transition: none !important;
  }

  .stat-card::before {
    display: none;
  }
}

/* ========== 卡片样式 ========== */
.exam-card {
  background: var(--bg-card);
  border-radius: var(--radius-lg);
  padding: var(--space-lg);
  margin-bottom: var(--space-lg);
  box-shadow: var(--shadow-md);
  border: 1px solid var(--border-light);
}

.card-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: var(--space-md);
  padding-bottom: var(--space-sm);
  border-bottom: 1px solid var(--border-light);
}

.card-header h2 {
  margin: 0;
  font-size: var(--font-xl);
  color: var(--text-primary);
  font-weight: var(--weight-semibold);
  display: flex;
  align-items: center;
  gap: var(--space-sm);
}

.draft-hint {
  font-size: var(--font-sm);
  color: var(--text-disabled);
  margin-left: auto;
  font-style: italic;
  display: flex;
  align-items: center;
  gap: var(--space-xs);
}

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

/* ========== 面板标题栏（统一白色体系）========== */
.exam-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: var(--space-sm) var(--space-lg);
  margin-bottom: var(--space-lg);
  background: var(--bg-card);
  border-radius: var(--radius-lg);
  border: 1px solid var(--border-light);
  box-shadow: var(--shadow-sm);
}

.exam-title {
  margin: 0;
  font-size: var(--font-2xl);
  color: var(--text-primary);
  font-weight: var(--weight-bold);
  display: flex;
  align-items: center;
  gap: var(--space-sm);
}

.user-info {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  font-size: var(--font-lg);
  color: var(--text-secondary);
}

.draft-card {
  border-left: 4px solid var(--color-warning);
  background: var(--bg-warning);
}

.draft-card .card-header {
  background: rgba(230, 119, 0, 0.08);
  border-radius: var(--radius-md) var(--radius-md) 0 0;
  padding: var(--space-md) var(--card-padding);
}

.draft-footer {
  margin-top: var(--space-md);
  padding: 12px 16px;
  background: #fff7e6;
  border-radius: var(--radius-md);
  border: 1px solid #ffd591;
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  font-size: var(--font-md);
  color: var(--color-warning);
}

.draft-warning {
  font-size: var(--font-lg);
}

.header-actions {
  display: flex;
  gap: var(--space-sm);
}

/* ========== 表单样式 ========== */
.form-group {
  margin-bottom: var(--form-gap);
}

.form-label {
  display: block;
  font-size: var(--font-md);
  font-weight: var(--weight-semibold);
  color: var(--text-primary);
  margin-bottom: var(--space-xs);
}

.form-input {
  width: 100%;
  height: var(--input-height);
  padding: 0 var(--space-md);
  border: 1px solid var(--border-medium);
  border-radius: var(--radius-md);
  font-size: var(--font-md);
  transition: all var(--transition-normal);
  background: var(--bg-container);
  box-sizing: border-box;
}

.form-input:focus {
  outline: none;
  border-color: var(--color-primary);
  box-shadow: 0 0 0 2px rgba(33, 37, 41, 0.1);
  background: var(--bg-card);
}

/* ========== 统一选择器样式 ========== */
.form-select {
  width: 100%;
  height: var(--input-height);
  padding: 0 28px 0 var(--space-md);
  border: 1px solid var(--border-medium);
  border-radius: var(--radius-md);
  background: var(--bg-container) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' 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 10px center;
  background-size: 10px;
  font-size: var(--font-md);
  color: var(--text-primary);
  cursor: pointer;
  transition: all 0.25s ease;
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
  box-sizing: border-box;
}

.form-select:hover {
  border-color: var(--border-dark);
  background-color: var(--bg-card);
  box-shadow: var(--shadow-sm);
}

.form-select:focus {
  outline: none;
  border-color: var(--color-primary);
  box-shadow: 0 0 0 3px rgba(33, 37, 41, 0.1);
  background-color: var(--bg-card);
}

.filter-select:focus {
  outline: none;
  border-color: var(--color-primary);
  box-shadow: 0 0 0 3px rgba(33, 37, 41, 0.1);
  background-color: var(--bg-card);
}

/* ========== 必填标记 ========== */
.required-mark {
  color: var(--color-error);
  margin-left: 2px;
}

/* ========== 已选知识库信息 ========== */
.collection-info {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  margin-top: 10px;
  padding: var(--space-sm) var(--space-lg);
  background: var(--bg-info);
  border: 1px solid var(--border-medium);
  border-radius: var(--radius-md);
  font-size: var(--font-md);
  color: var(--color-info);
}

.collection-icon {
  font-size: var(--font-xl);
  display: flex;
  align-items: center;
}

.selected-file-info {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  margin-top: var(--space-xs);
  padding: var(--space-xs) var(--space-sm);
  background: var(--bg-success);
  border: 1px solid var(--border-medium);
  border-radius: var(--radius-md);
  font-size: var(--font-md);
}

.file-info-icon {
  font-size: var(--font-xl);
  display: flex;
  align-items: center;
}

.file-info-name {
  font-weight: var(--weight-semibold);
  color: var(--text-primary);
  flex: 1;
}

.file-info-version {
  color: var(--color-success);
  font-weight: var(--weight-bold);
  font-size: var(--font-sm);
  background: var(--bg-success);
  padding: 2px 8px;
  border-radius: 10px;
  border: 1px solid var(--border-medium);
}

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

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

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

.type-count-label.active {
  background: var(--bg-active);
  border-color: var(--color-primary);
  color: var(--color-primary);
}

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

.type-count-total {
  margin-top: var(--space-md);
  padding: 10px 16px;
  background: var(--bg-container);
  border-radius: var(--radius-md);
  font-size: var(--font-lg);
  color: var(--text-secondary);
  text-align: center;
}

.type-count-total strong {
  color: var(--color-primary);
  font-size: var(--font-xl);
}

/* ========== 生成弹窗样式 ========== */
.generate-modal-content {
  text-align: center;
  padding: var(--space-lg) var(--space-sm);
}

.generate-spinner {
  position: relative;
  width: 64px;
  height: 64px;
  margin: 0 auto var(--space-md);
}

.spinner-ring {
  position: absolute;
  inset: 0;
  border: 3px solid var(--border-light);
  border-top-color: var(--color-primary);
  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: 24px;
}

.generate-status-icon {
  font-size: 44px;
  margin-bottom: var(--space-sm);
}

.generate-title {
  font-size: var(--font-lg);
  font-weight: var(--weight-semibold);
  color: var(--text-primary);
  margin-bottom: var(--space-xs);
}

.generate-detail {
  color: var(--text-secondary);
  font-size: var(--font-md);
  margin-top: var(--space-sm);
  min-height: 18px;
}

/* ========== 等待动画样式（替代进度条）========== */
.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: var(--color-primary);
  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: var(--space-lg);
  padding: 14px 18px;
  background: var(--bg-success);
  border-radius: var(--radius-lg);
  border: 1px solid var(--border-medium);
}

.result-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-size: var(--font-lg);
}

.result-label {
  color: var(--text-secondary);
}

.result-value.success {
  color: var(--color-success);
  font-weight: var(--weight-bold);
  font-size: var(--font-xl);
}

@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: var(--space-xs);
  padding: 24px 36px;
  border-radius: var(--radius-xl);
  border: 3px solid var(--border-medium);
  background: var(--bg-container);
}

.grade-score-circle.excellent {
  border-color: var(--color-success);
  background: var(--bg-success);
}

.grade-score-circle.good {
  border-color: var(--color-info);
  background: var(--bg-info);
}

.grade-score-circle.poor {
  border-color: var(--color-error);
  background: var(--bg-error);
}

.score-value {
  font-size: 42px;
  font-weight: var(--weight-extrabold);
  color: var(--color-primary);
}

.grade-score-circle.excellent .score-value { color: var(--color-success); }
.grade-score-circle.poor .score-value { color: var(--color-error); }

.score-divider {
  font-size: var(--font-xl);
  color: var(--text-tertiary);
  margin: 0 2px;
}

.score-max {
  font-size: var(--font-2xl);
  font-weight: var(--weight-medium);
  color: var(--text-tertiary);
}

.grade-rate {
  font-size: var(--font-xl);
  color: var(--text-secondary);
  margin-top: var(--space-sm);
  font-weight: var(--weight-medium);
}

.grade-results-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  max-height: 300px;
  overflow-y: auto;
}

.grade-result-item {
  padding: var(--space-sm) var(--space-md);
  border-radius: var(--radius-md);
  border: 1px solid var(--border-medium);
  background: var(--bg-container);
}

.grade-result-item.correct {
  border-color: var(--border-medium);
  background: var(--bg-success);
}

.grade-result-item.wrong {
  border-color: var(--border-medium);
  background: var(--bg-error);
}

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

.grade-item-num {
  font-weight: var(--weight-semibold);
  color: var(--text-primary);
  font-size: var(--font-md);
}

.grade-item-score {
  font-weight: var(--weight-bold);
  color: var(--color-primary);
  font-size: var(--font-lg);
}

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

.grade-item-tag.correct {
  background: var(--bg-success);
  color: var(--color-success);
  border: 1px solid var(--border-medium);
}

.grade-item-tag.wrong {
  background: var(--bg-error);
  color: var(--color-error);
  border: 1px solid var(--border-medium);
}

.grade-item-tag.subjective {
  background: var(--bg-info);
  color: var(--color-info);
  border: 1px solid var(--border-medium);
}

.grade-item-feedback {
  font-size: var(--font-md);
  color: var(--text-secondary);
  padding: var(--space-sm) 0;
}

.grade-item-ai-feedback {
  font-size: var(--font-md);
  color: var(--text-secondary);
  padding: 10px 14px;
  background: var(--bg-card);
  border-radius: var(--radius-md);
  border-left: 3px solid var(--color-primary);
  line-height: 1.6;
  margin-top: var(--space-sm);
}

.grade-item-detail-feedback {
  font-size: var(--font-sm);
  color: var(--text-tertiary);
  padding: var(--space-sm) var(--space-md);
  background: var(--bg-container);
  border-radius: var(--radius-md);
  margin-top: var(--space-sm);
}

.grade-item-detail-feedback strong {
  display: block;
  margin-bottom: 4px;
  color: var(--text-secondary);
}

.scoring-breakdown {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.scoring-item {
  padding: 2px 0;
  color: var(--text-tertiary);
}

.grade-empty {
  text-align: center;
  padding: var(--space-xl);
  color: var(--text-tertiary);
}

/* ========== 答题相关样式 ========== */
.question-item.answered {
  border-color: var(--border-medium);
  background: var(--bg-success);
}

.answered-badge {
  font-size: var(--font-sm);
  color: var(--color-success);
  font-weight: var(--weight-semibold);
  background: var(--bg-success);
  padding: 2px 10px;
  border-radius: 12px;
  border: 1px solid var(--border-medium);
  margin-left: auto;
}

.option-radio.selected,
.option-checkbox.selected {
  border-color: var(--color-primary);
  background: var(--bg-active);
}

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

.fill-blank-answer .form-label {
  margin-bottom: var(--space-sm);
  font-size: var(--font-md);
  color: var(--text-secondary);
}

.subjective-answer {
  padding: var(--space-lg) 0;
}

.subjective-answer .form-label {
  margin-bottom: var(--space-sm);
  font-size: var(--font-md);
  color: var(--text-secondary);
}

.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: var(--font-sm);
  color: var(--text-tertiary);
  margin-top: var(--space-xs);
}

.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: var(--border-dark);
  box-shadow: var(--shadow-sm);
}

.filter-select-sm {
  width: auto;
  min-width: 110px;
  height: 34px;
  padding: 0 28px 0 10px;
  border-radius: var(--radius-md);
  font-size: var(--font-md);
  border: 1px solid var(--border-medium);
  background-color: var(--bg-card);
  cursor: pointer;
  transition: all var(--transition-fast);
}

.filter-select-sm:hover {
  border-color: var(--color-primary);
}

.filter-select-sm:focus {
  border-color: var(--color-primary);
  outline: none;
  box-shadow: 0 0 0 2px rgba(33, 37, 41, 0.1);
}

/* Ant Design Select 统一样式 */
.form-select :deep(.ant-select-selector) {
  height: var(--input-height) !important;
  min-height: var(--input-height) !important;
  max-height: var(--input-height) !important;
  padding: 0 28px 0 var(--space-md) !important;
  border-radius: var(--radius-md) !important;
  border: 1px solid var(--border-medium) !important;
  background: var(--bg-container) !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: var(--border-dark) !important;
  background: var(--bg-card) !important;
  box-shadow: var(--shadow-sm) !important;
}

.form-select :deep(.ant-select-focused .ant-select-selector) {
  border-color: var(--color-primary) !important;
  box-shadow: 0 0 0 3px rgba(33, 37, 41, 0.1) !important;
  background: var(--bg-card) !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: var(--font-lg);
  color: var(--text-primary);
  font-weight: var(--weight-medium);
  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: var(--font-lg);
  color: var(--text-disabled);
  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: var(--color-primary);
  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: var(--bg-active);
}

.checkbox-item input[type="checkbox"] {
  width: 18px;
  height: 18px;
  margin-right: var(--space-sm);
  accent-color: var(--color-primary);
}

.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: var(--color-primary);
  color: var(--text-inverse);
}

.btn-primary:hover:not(:disabled) {
  background: var(--color-primary-hover);
  transform: translateY(-2px);
  box-shadow: var(--shadow-md);
}

.btn-secondary {
  background: var(--bg-card);
  color: var(--text-secondary);
  border: 1.5px solid var(--border-medium);
}

.btn-secondary:hover:not(:disabled) {
  background: var(--bg-hover);
  border-color: var(--border-dark);
}

.btn-success {
  background: var(--color-success);
  color: var(--text-inverse);
}

.btn-success:hover:not(:disabled) {
  opacity: 0.9;
  transform: translateY(-2px);
  box-shadow: var(--shadow-md);
}

.btn-info {
  background: var(--color-info);
  color: var(--text-inverse);
}

.btn-info:hover:not(:disabled) {
  opacity: 0.9;
  transform: translateY(-2px);
  box-shadow: var(--shadow-md);
}

.btn-warning {
  background: var(--color-warning);
  color: var(--text-inverse);
}

.btn-danger {
  background: var(--color-error);
  color: var(--text-inverse);
}

.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: var(--color-primary) !important;
  color: var(--text-inverse) !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;
}

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

.dot:nth-child(1) {
  animation-delay: 0s;
}

.dot:nth-child(2) {
  animation-delay: 0.2s;
}

.dot:nth-child(3) {
  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: var(--space-md);
  margin-top: var(--space-2xl);
  padding-top: var(--space-xl);
  border-top: 1px solid var(--border-light);
}

/* ========== 题目列表样式 ========== */
.questions-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-lg);
}

.question-item {
  background: var(--bg-container);
  border: 1px solid var(--border-medium);
  border-radius: var(--radius-lg);
  padding: var(--space-xl);
  transition: all var(--transition-normal);
}

.question-item:hover {
  box-shadow: var(--shadow-lg);
  border-color: var(--border-default);
}

.question-item.answer-mode {
  background: var(--bg-card);
  border: 2px solid var(--color-primary);
}

.question-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: var(--space-lg);
  padding-bottom: var(--space-md);
  border-bottom: 1px dashed var(--border-light);
}

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

.question-number {
  font-size: var(--font-md);
  color: var(--text-tertiary);
}

.question-content {
  font-size: var(--font-lg);
  line-height: 1.8;
  color: var(--text-primary);
  margin-bottom: var(--space-lg);
  padding: 12px 16px;
  background: var(--bg-card);
  border-radius: var(--radius-md);
  border-left: 4px solid var(--color-primary);
}

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

.type-badge.single {
  background: var(--bg-info);
  color: var(--color-info);
  border: 1px solid var(--border-medium);
}

.type-badge.multiple {
  background: var(--bg-success);
  color: var(--color-success);
  border: 1px solid var(--border-medium);
}

.type-badge.judgment {
  background: var(--bg-warning);
  color: var(--color-warning);
  border: 1px solid var(--border-medium);
}

.type-badge.essay {
  background: var(--bg-error);
  color: var(--color-error);
  border: 1px solid var(--border-medium);
}

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

.status-badge.status-info {
  background: var(--bg-info);
  color: var(--color-info);
}

.status-badge.status-warning {
  background: var(--bg-warning);
  color: var(--color-warning);
}

/* ========== 选项样式 ========== */
.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: var(--space-sm);
  margin-bottom: var(--space-lg);
  padding: var(--space-xs) 0;
  flex-wrap: wrap;
  align-items: center;
}

.train-tab {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-sm);
  padding: var(--space-sm) var(--space-xl);
  border: 1.5px solid var(--border-light);
  border-radius: var(--radius-md);
  background: var(--bg-card);
  font-size: var(--font-lg);
  font-weight: var(--weight-medium);
  cursor: pointer;
  transition: all var(--transition-normal);
  color: var(--text-secondary);
  min-height: 40px;
  line-height: 1.5;
  white-space: nowrap;
}

.train-tab:hover {
  border-color: var(--color-primary);
  color: var(--color-primary);
  background: var(--bg-hover);
  transform: translateY(-1px);
  box-shadow: var(--shadow-sm);
}

.train-tab.active {
  background: var(--bg-active);
  color: var(--text-primary);
  border-color: var(--color-primary);
  font-weight: var(--weight-semibold);
  box-shadow: inset 0 -2px 0 var(--color-primary);
}

/* ========== 闯关地图 ========== */
.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;
}

/* ========== 空状态 ========== */
.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;
  }
}

/* ========== 编辑题目弹窗样式 ========== */
.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: var(--space-sm);
  margin-bottom: var(--space-lg);
  padding: var(--space-xs) 0;
  flex-wrap: wrap;
  align-items: center;
}

.sub-tab {
  display: inline-flex;
  align-items: center;
  gap: var(--space-sm);
  padding: var(--space-sm) var(--space-xl);
  border: 1.5px solid var(--border-light);
  border-radius: var(--radius-md);
  background: var(--bg-card);
  font-size: var(--font-lg);
  font-weight: var(--weight-medium);
  cursor: pointer;
  transition: all var(--transition-normal);
  color: var(--text-secondary);
  min-height: 40px;
  line-height: 1.5;
  white-space: nowrap;
}

.sub-tab:hover:not(:disabled) {
  border-color: var(--color-primary);
  color: var(--color-primary);
  background: var(--bg-hover);
  transform: translateY(-1px);
  box-shadow: var(--shadow-sm);
}

.sub-tab.active {
  background: var(--bg-active);
  color: var(--text-primary);
  border-color: var(--color-primary);
  font-weight: var(--weight-semibold);
  box-shadow: inset 0 -2px 0 var(--color-primary);
}

.sub-tab:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

.tab-badge {
  background: var(--color-error);
  color: var(--text-inverse);
  font-size: var(--font-xs);
  min-width: 18px;
  height: 18px;
  border-radius: 9px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0 5px;
  font-weight: var(--weight-semibold);
  margin-left: 2px;
}

/* 待考试卡片 */
.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: var(--color-primary); box-shadow: var(--shadow-lg); 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: 16px 12px;
  position: relative;
}

.taking-header {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  padding: 10px 24px;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  border-radius: 14px; color: #fff;
  margin-bottom: 12px;
  box-shadow: 0 6px 18px rgba(102, 126, 234, 0.25);
  position: relative; overflow: hidden;
  gap: 16px;
}
.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: 6px 14px;
  border-radius: 6px;
  font-weight: 500;
  font-size: 13px;
  line-height: 1.4;
  height: 32px;
  flex-shrink: 0;
  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: 15px;
  letter-spacing: 0.5px;
  line-height: 1.4;
  position: relative; z-index: 1;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}
.taking-progress {
  font-size: 13px;
  opacity: 0.95;
  font-weight: 500;
  background: rgba(255,255,255,0.15);
  padding: 3px 11px;
  border-radius: 14px;
  backdrop-filter: blur(10px);
  line-height: 1.6;
  white-space: nowrap;
  height: 24px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.taking-body {
  background: #fff;
  border: 1px solid #e8e8e8;
  border-radius: 0 16px 16px 0;
  padding: 36px 40px;
  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: var(--color-primary-light); }

.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: 0 16px 16px 0;
  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: 0;
  align-items: stretch;
  min-height: 520px;
}

.answer-drawer {
  width: 240px;
  min-width: 240px;
  background: #fafbfc;
  border-right: 1px solid #e8e8e8;
  border-radius: 16px 0 0 16px;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  position: relative;
}
.answer-drawer.collapsed {
  width: 40px;
  min-width: 40px;
}

.drawer-toggle {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 36px;
  cursor: pointer;
  border-bottom: 1px solid var(--border-light);
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  color: #fff;
  transition: all 0.25s ease;
  user-select: none;
  position: sticky;
  top: 0;
  z-index: 2;
}
.drawer-toggle:hover {
  background: linear-gradient(135deg, #5a6fd6 0%, #6a4196 100%);
}
.toggle-icon {
  font-size: var(--font-lg);
  font-weight: bold;
}
.toggle-label {
  font-size: var(--font-xs);
  writing-mode: vertical-rl;
  text-orientation: mixed;
  margin-left: 2px;
  letter-spacing: 2px;
}

.answer-drawer.collapsed .drawer-toggle {
  height: 100%;
  border-bottom: none;
  border-radius: var(--radius-lg) 0 0 var(--radius-lg);
  justify-content: center;
  gap: 0;
}
.answer-drawer.collapsed .toggle-icon {
  font-size: var(--font-xl);
}
.answer-drawer.collapsed .toggle-label {
  display: none;
}

.drawer-content {
  flex: 1;
  overflow-y: auto;
  padding: var(--space-sm);
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}
.drawer-content::-webkit-scrollbar { width: 5px; }
.drawer-content::-webkit-scrollbar-track { background: transparent; }
.drawer-content::-webkit-scrollbar-thumb { background: #d9d9d9; border-radius: 3px; }
.drawer-content::-webkit-scrollbar-thumb:hover { background: #bfbfbf; }

.answer-card-header h3 {
  margin: 0;
  font-size: 15px;
  font-weight: 600;
  color: #262626;
  padding-bottom: 10px;
  border-bottom: 1px solid #e8e8e8;
}

.answer-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 8px;
}

.answer-cell {
  aspect-ratio: 1;
  border: 2px solid #e8e8e8;
  border-radius: 8px;
  background: #fff;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
  transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
  font-size: 14px;
  font-weight: 500;
  color: #595959;
  user-select: none;
  outline: none;
}
.answer-cell:hover {
  border-color: #1890ff;
  color: #1890ff;
  transform: scale(1.05);
  box-shadow: 0 2px 8px rgba(24, 144, 255, 0.15);
}
.answer-cell.is-current {
  border-color: #1890ff;
  background: #e6f4ff;
  color: #1890ff;
  font-weight: 700;
  box-shadow: 0 0 0 3px rgba(24, 144, 255, 0.15), 0 2px 8px rgba(24, 144, 255, 0.1);
  transform: scale(1.08);
  z-index: 1;
}
.answer-cell.is-answered {
  border-color: #52c41a;
  background: #f6ffed;
  color: #389e0d;
}
.answer-cell.is-answered.is-current {
  border-color: #1890ff;
  background: linear-gradient(135deg, #e6f4ff 0%, #f6ffed 100%);
}
.answer-cell.is-unanswered {
  background: #fff;
  color: #8c8c8c;
}
.cell-num {
  line-height: 1;
}
.cell-check {
  position: absolute;
  top: 2px;
  right: 2px;
  font-size: 9px;
  color: #52c41a;
  line-height: 1;
}

.answer-legend {
  display: flex;
  justify-content: center;
  gap: 16px;
  padding-top: 10px;
  border-top: 1px solid #e8e8e8;
  margin-top: auto;
}
.legend-item {
  display: flex;
  align-items: center;
  gap: 5px;
  font-size: 12px;
  color: #8c8c8c;
}
.legend-dot {
  width: 12px;
  height: 12px;
  border-radius: 3px;
  border: 2px solid;
}
.legend-dot.current { border-color: #1890ff; background: #e6f4ff; }
.legend-dot.answered { border-color: #52c41a; background: #f6ffed; }
.legend-dot.unanswered { border-color: #d9d9d9; background: #fff; }

/* 右侧主区域 */
.taking-main {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-width: 0;
}
.taking-main .taking-body {
  flex: 1;
}
.taking-main .taking-footer {
  flex-shrink: 0;
}

/* 答题加载中覆盖层 */
.starting-overlay {
  position: absolute;
  inset: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(255, 255, 255, 0.85);
  backdrop-filter: blur(4px);
  border-radius: inherit;
}
.starting-content {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
}
.starting-text {
  font-size: 15px;
  color: #555;
  font-weight: 500;
}

.taking-blur {
  transition: opacity 0.25s ease, filter 0.25s ease;
}
.taking-blur.is-starting {
  opacity: 0.4;
  pointer-events: none;
}

/* 头部信息区 */
.taking-header-info {
  display: flex;
  align-items: center;
  gap: 10px;
}
.answered-count {
  font-size: 13px;
  opacity: 0.95;
  font-weight: 500;
  background: rgba(255,255,255,0.15);
  padding: 3px 11px;
  border-radius: 14px;
  backdrop-filter: blur(10px);
  line-height: 1.6;
  white-space: nowrap;
  height: 24px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

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

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

  .taking-header {
    padding: 12px 18px;
    gap: 12px;
  }

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

  .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;
  }

  .answer-drawer {
    width: 200px;
    min-width: 200px;
  }
  .answer-drawer.collapsed {
    width: 48px;
    min-width: 48px;
  }
  .answer-grid {
    grid-template-columns: repeat(4, 1fr);
    gap: 6px;
  }
}

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

  .taking-header {
    padding: 12px 16px;
    gap: 10px;
    grid-template-columns: auto 1fr auto;
  }

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

  .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;
  }

  /* 手机端答题卡默认收起 */
  .taking-layout {
    flex-direction: column;
  }

  .answer-drawer {
    width: 100% !important;
    min-width: 100% !important;
    border-right: none;
    border-bottom: 1px solid #e8e8e8;
    border-radius: 12px 12px 0 0;
    max-height: 180px;
  }
  .answer-drawer.collapsed {
    max-height: 48px;
  }

  .drawer-toggle {
    height: 40px;
    border-radius: 12px 12px 0 0;
  }
  .toggle-label {
    display: none;
  }
  .drawer-content {
    padding: 12px 10px;
  }

  .answer-grid {
    grid-template-columns: repeat(6, 1fr);
    gap: 5px;
  }
  .answer-cell {
    font-size: 12px;
    border-radius: 6px;
  }
  .cell-check { font-size: 8px; }

  .taking-main {
    min-width: 0;
  }
}

/* 小屏手机（< 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); }
}

/* 禁用状态的视觉反馈 */
.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: var(--color-primary); }
.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: var(--color-primary); color: var(--text-primary); }
.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: var(--space-xs); }
.ep-action-btns { display: flex; gap: var(--space-xs); }

/* 空状态提示 */
.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: var(--space-xs) 0;
}
.publish-paper-modal .publish-paper-info {
  background: linear-gradient(135deg, #f0f9ff, #e6f7ff);
  border: 1px solid #bae7ff;
  border-radius: var(--radius-md);
  padding: var(--space-sm) var(--space-md);
  margin-bottom: var(--space-md);
}
.publish-paper-modal .publish-paper-name {
  font-size: var(--font-lg);
  font-weight: 600;
  color: #1a1a2e;
  margin-bottom: var(--space-xs);
}
.publish-paper-modal .publish-paper-stats {
  display: flex;
  gap: var(--space-md);
  color: #666;
  font-size: var(--font-md);
}
.publish-paper-modal .publish-paper-stats span {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
}
.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>