| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761 |
- {% extends "layout.html" %}
- {% block page_title %}审核题目{% endblock %}
- {% block content %}
- <div class="flex gap-6">
- <!-- 左侧目录索引 -->
- <div class="w-96 flex-shrink-0">
- <div class="apple-card p-6 sticky top-4 max-h-[calc(100vh-2rem)] overflow-y-auto">
- <div class="flex items-center justify-between mb-6 pb-3 border-b border-gray-200">
- <h2 class="text-lg font-bold text-gray-800">未审核知识点</h2>
- </div>
- <nav class="space-y-1">
- {% macro render_kp_node(node, level) %}
- <div class="kp-node-item mb-1" data-kp-code="{{ node.kp_code }}" data-level="{{ level }}">
- <div class="flex items-center gap-1 group">
- {% if node.children|length > 0 %}
- <button
- onclick="toggleKpNode('{{ node.kp_code }}'); event.stopPropagation();"
- class="w-6 h-6 rounded flex items-center justify-center text-gray-500 hover:text-orange-600 hover:bg-orange-50 transition-all flex-shrink-0 kp-expand-btn"
- data-expanded="{% if level == 0 %}true{% else %}false{% endif %}"
- data-kp-code="{{ node.kp_code }}">
- {% if level == 0 %}
- <i class="ri-subtract-line text-sm"></i>
- {% else %}
- <i class="ri-add-line text-sm"></i>
- {% endif %}
- </button>
- {% else %}
- <div class="w-6 h-6 flex items-center justify-center flex-shrink-0">
- <div class="w-1.5 h-1.5 rounded-full bg-gray-400"></div>
- </div>
- {% endif %}
- <a href="javascript:void(0)"
- onclick="loadPendingQuestionsByKp('{{ node.kp_code }}', '{{ node.name }}', this); return false;"
- data-kp-code="{{ node.kp_code }}"
- data-kp-name="{{ node.name }}"
- class="flex-1 block px-3 py-2 rounded-lg hover:bg-gradient-to-r hover:from-orange-50 hover:to-amber-50 transition-all border-l-2 border-transparent hover:border-orange-400 kp-link">
- <div class="flex items-center gap-2 flex-nowrap">
- {% if level == 0 %}
- <span class="bg-gradient-to-r from-slate-600 to-slate-700 text-white px-2 py-0.5 rounded text-xs font-bold shadow-sm flex-shrink-0">章</span>
- {% elif level == 1 %}
- <span class="bg-gradient-to-r from-blue-500 to-blue-600 text-white px-2 py-0.5 rounded text-xs font-bold shadow-sm flex-shrink-0">节</span>
- {% else %}
- <span class="bg-gray-200 text-gray-700 px-1.5 py-0.5 rounded text-xs font-medium flex-shrink-0">小节</span>
- {% endif %}
- <span class="text-sm {% if level == 0 %}font-bold text-gray-800{% elif level == 1 %}font-semibold text-gray-800{% else %}font-normal text-gray-600{% endif %} group-hover:text-orange-600 transition-colors flex-1 min-w-0 truncate">{{ node.name }}</span>
- {% set pending_count = node.total_pending_count|default(node.pending_count|default(0)) %}
- {% if pending_count > 0 %}
- <span class="bg-orange-100 text-orange-700 px-2 py-0.5 rounded-full text-xs font-bold flex-shrink-0 ml-1">{{ pending_count }}</span>
- {% endif %}
- </div>
- </a>
- </div>
- {% if node.children|length > 0 %}
- <div class="kp-children ml-8 mt-1 {% if level >= 1 %}hidden{% endif %}" id="kp-children-{{ node.kp_code }}" data-level="{{ level }}">
- {% for child in node.children %}
- {{ render_kp_node(child, level + 1) }}
- {% endfor %}
- </div>
- {% endif %}
- </div>
- {% endmacro %}
-
- <!-- 其他题目节点 -->
- {% if other_questions_count|default(0) > 0 %}
- <div class="kp-node-item mb-2 pb-2 border-b border-gray-200" data-kp-code="null" data-level="-1">
- <a href="javascript:void(0)"
- onclick="loadPendingQuestionsByKp('null', '其他题目', this); return false;"
- data-kp-code="null"
- data-kp-name="其他题目"
- class="flex-1 block px-3 py-2 rounded-lg hover:bg-gradient-to-r hover:from-purple-50 hover:to-pink-50 transition-all border-l-2 border-transparent hover:border-purple-400 kp-link">
- <div class="flex items-center gap-2 flex-nowrap">
- <span class="bg-gradient-to-r from-purple-500 to-pink-500 text-white px-2 py-0.5 rounded text-xs font-bold shadow-sm flex-shrink-0">其他</span>
- <span class="text-sm font-bold text-gray-800 group-hover:text-purple-600 transition-colors flex-1 min-w-0 truncate">其他题目</span>
- <span class="bg-orange-100 text-orange-700 px-2 py-0.5 rounded-full text-xs font-bold flex-shrink-0 ml-1">{{ other_questions_count }}</span>
- </div>
- </a>
- </div>
- {% endif %}
-
- {% if kp_tree %}
- {% for root_node in kp_tree %}
- {{ render_kp_node(root_node, 0) }}
- {% endfor %}
- {% else %}
- <div class="text-sm text-gray-500 text-center py-8">暂无未审核题目</div>
- {% endif %}
- </nav>
- </div>
- </div>
-
- <!-- 右侧题目列表区域 -->
- <div class="flex-1">
- <!-- 筛选栏 -->
- <div id="filter-bar" class="apple-card p-4 mb-4">
- <div class="flex items-center gap-4 flex-wrap">
- <span class="text-sm font-medium text-gray-700">筛选:</span>
-
- <!-- 创建人筛选 -->
- <div class="flex items-center gap-2">
- <label class="text-sm text-gray-600">创建人:</label>
- <select id="filter-create-by" class="px-3 py-1.5 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent">
- <option value="">全部</option>
- {% for creator in creators|default([]) %}
- <option value="{{ creator }}">{{ creator }}</option>
- {% endfor %}
- </select>
- </div>
-
- <!-- 创建时间筛选 -->
- <div class="flex items-center gap-2">
- <label class="text-sm text-gray-600">创建时间:</label>
- <input type="date" id="filter-created-at" class="px-3 py-1.5 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent" />
- </div>
-
- <!-- 重置按钮 -->
- <button onclick="resetFilters()" class="px-4 py-1.5 bg-gray-100 text-gray-700 rounded-lg text-sm hover:bg-gray-200 transition-colors">
- 重置
- </button>
- </div>
- </div>
-
- <div id="questions-container" class="space-y-4">
- <div class="apple-card p-12 text-center">
- <div class="text-gray-400 mb-4">
- <i class="ri-file-check-line text-6xl"></i>
- </div>
- <h3 class="text-lg font-bold text-gray-600 mb-2">请选择左侧知识点或设置筛选条件</h3>
- <p class="text-sm text-gray-500 mb-6">点击知识点查看该知识点下的未审核题目,或使用筛选条件查看所有题目</p>
- </div>
- </div>
- </div>
- </div>
- <script>
- // 知识点目录折叠/展开功能
- function toggleKpNode(kpCode) {
- const childrenContainer = document.getElementById(`kp-children-${kpCode}`);
- const expandBtn = document.querySelector(`.kp-expand-btn[data-kp-code="${kpCode}"]`);
-
- if (!childrenContainer || !expandBtn) return;
-
- const isExpanded = expandBtn.getAttribute('data-expanded') === 'true';
- const icon = expandBtn.querySelector('i');
-
- if (isExpanded) {
- // 折叠
- childrenContainer.classList.add('hidden');
- expandBtn.setAttribute('data-expanded', 'false');
- if (icon) {
- icon.className = 'ri-add-line text-sm';
- }
- } else {
- // 展开
- childrenContainer.classList.remove('hidden');
- expandBtn.setAttribute('data-expanded', 'true');
- if (icon) {
- icon.className = 'ri-subtract-line text-sm';
- }
- }
- }
- // 存储当前知识点的代码和名称
- let currentKpCode = null;
- let currentKpName = null;
- // 加载指定知识点的未审核题目列表(支持分页和筛选)
- function loadPendingQuestionsByKp(kpCode, kpName, linkElement, page = null) {
- const container = document.getElementById('questions-container');
- if (!container) return;
-
- // 保存当前知识点信息
- currentKpCode = kpCode;
- currentKpName = kpName;
-
- // 更新选中状态
- document.querySelectorAll('.kp-link').forEach(link => {
- link.classList.remove('bg-orange-50', 'border-orange-400');
- });
- if (linkElement) {
- linkElement.classList.add('bg-orange-50', 'border-orange-400');
- }
-
- // 显示加载状态
- container.innerHTML = `
- <div class="apple-card p-12 text-center">
- <div class="text-orange-500 mb-4">
- <i class="ri-loader-4-line text-6xl animate-spin"></i>
- </div>
- <p class="text-gray-600">正在加载未审核题目...</p>
- </div>
- `;
-
- // 如果没有指定页码,从URL参数获取
- if (page === null) {
- const urlParams = new URLSearchParams(window.location.search);
- const pageParam = urlParams.get('page');
- page = pageParam ? parseInt(pageParam) : 1;
- }
-
- // 获取筛选参数
- const createByFilter = document.getElementById('filter-create-by')?.value || '';
- const createdAtFilter = document.getElementById('filter-created-at')?.value || '';
-
- // 构建请求URL(添加分页和筛选参数)
- let apiUrl = `/api/pending_questions_by_kp/${encodeURIComponent(kpCode)}`;
- const params = new URLSearchParams();
- if (page > 1) {
- params.append('page', page);
- }
- if (createByFilter) {
- params.append('create_by', createByFilter);
- }
- if (createdAtFilter) {
- params.append('created_at', createdAtFilter);
- }
- if (params.toString()) {
- apiUrl += '?' + params.toString();
- }
-
- // 请求未审核题目列表
- fetch(apiUrl)
- .then(response => response.json())
- .then(data => {
- if (!data.success) {
- throw new Error(data.error || '加载失败');
- }
-
- const questions = data.questions || [];
- const kpName = data.kp_name || kpCode;
-
- // 获取分页信息(从API返回)
- const pagination = data.pagination || {};
- const currentPage = pagination.page || 1;
- const totalPages = pagination.total_pages || 1;
- const totalCount = pagination.total_count || questions.length;
- const pageSize = pagination.page_size || 20;
- const startIndex = (currentPage - 1) * pageSize;
- const endIndex = Math.min(startIndex + pageSize, totalCount);
-
- // 渲染题目列表
- let html = '';
-
- html += `<div class="space-y-3 ${kpCode === 'null' || kpCode === '' ? '' : 'mt-6'}">`;
-
- // 如果没有题目,显示提示信息
- if (questions.length === 0) {
- html += `
- <div class="apple-card p-12 text-center">
- <div class="text-gray-400 mb-4">
- <i class="ri-file-list-line text-6xl"></i>
- </div>
- <h3 class="text-lg font-bold text-gray-600 mb-2">该知识点下暂无未审核题目</h3>
- <p class="text-sm text-gray-500">所有题目已审核完成</p>
- </div>
- `;
- } else {
- // 有题目时,渲染题目列表(完全按照题目管理页面的格式)
- questions.forEach(q => {
- // 审核状态(待审核)
- let auditBadge = '<span class="bg-orange-100 text-orange-700 px-2 py-0.5 rounded text-xs font-bold whitespace-nowrap">待审核</span>';
-
- // 难度标签
- let difficultyBadge = '';
- if (q.difficulty !== null && q.difficulty !== undefined) {
- const diff = parseFloat(q.difficulty);
- if (diff === 0.2 || Math.abs(diff - 0.2) < 0.1) {
- difficultyBadge = '<span class="px-2 py-0.5 rounded-full text-xs font-bold bg-green-100 text-green-700 border border-green-200 whitespace-nowrap">筑基</span>';
- } else if (diff === 0.4 || Math.abs(diff - 0.4) < 0.1) {
- difficultyBadge = '<span class="px-2 py-0.5 rounded-full text-xs font-bold bg-yellow-100 text-yellow-700 border border-yellow-200 whitespace-nowrap">提分</span>';
- } else if (diff === 0.7 || Math.abs(diff - 0.7) < 0.1) {
- difficultyBadge = '<span class="px-2 py-0.5 rounded-full text-xs font-bold bg-orange-100 text-orange-700 border border-orange-200 whitespace-nowrap">培优</span>';
- }
- }
-
- // 题型标签
- const typeMap = {'choice': '选择题', 'fill': '填空题', 'answer': '解答题'};
- const questionTypeText = typeMap[q.question_type] || q.question_type || '未分类';
-
- // 年级标签
- let gradeBadge = '';
- if (q.grade !== null && q.grade !== undefined) {
- const grade = parseInt(q.grade);
- if (grade === 1) {
- gradeBadge = '<span class="px-2 py-0.5 rounded-full text-xs font-bold bg-pink-100 text-pink-700 border border-pink-200 whitespace-nowrap">小学</span>';
- } else if (grade === 2) {
- gradeBadge = '<span class="px-2 py-0.5 rounded-full text-xs font-bold bg-blue-100 text-blue-700 border border-blue-200 whitespace-nowrap">初中</span>';
- } else if (grade === 3) {
- gradeBadge = '<span class="px-2 py-0.5 rounded-full text-xs font-bold bg-purple-100 text-purple-700 border border-purple-200 whitespace-nowrap">高中</span>';
- }
- }
-
- // 题干预览(去除HTML标签,只显示文本)
- const stemText = (q.stem || '').replace(/<[^>]*>/g, '').substring(0, 150);
-
- html += `
- <a href="/detail/${q.question_code}${currentKpCode ? '?kp_code=' + encodeURIComponent(currentKpCode) : ''}"
- class="apple-card p-4 block group hover:shadow-lg transition-all border-l-4 border-transparent hover:border-blue-500">
- <div class="flex items-center gap-4">
- <!-- 左侧:题号 -->
- <div class="flex-shrink-0">
- <span class="text-xs font-mono text-gray-500 bg-gray-100 px-3 py-1.5 rounded font-semibold">${q.question_code}</span>
- </div>
-
- <!-- 中间:题干内容 -->
- <div class="flex-1 min-w-0">
- <div class="text-sm text-gray-800 group-hover:text-blue-600 transition-colors line-clamp-1">
- ${stemText}${stemText.length >= 150 ? '...' : ''}
- </div>
- </div>
- <!-- 右侧:标签和操作 -->
- <div class="flex items-center gap-3 flex-shrink-0">
- <span class="text-xs text-gray-500 bg-gray-50 px-2 py-1 rounded">${questionTypeText}</span>
- ${gradeBadge}
- ${difficultyBadge}
- ${auditBadge}
- <span class="text-xs text-blue-600 font-medium opacity-0 group-hover:opacity-100 transition-opacity whitespace-nowrap">查看详情 →</span>
- </div>
- </div>
- </a>
- `;
- });
-
- // 添加翻页控件(完全按照题目管理页面的格式)
- if (totalPages > 1) {
- html += `
- <div class="mt-6 flex items-center justify-between">
- <div class="text-sm text-gray-600">
- 显示第 ${startIndex + 1}-${endIndex} 题,共 ${totalCount} 题
- </div>
- <div class="flex items-center gap-2">
- <button
- onclick="loadPendingQuestionsByKpPage('${kpCode}', ${currentPage - 1})"
- ${currentPage === 1 ? 'disabled' : ''}
- class="px-4 py-2 rounded-lg text-sm font-medium transition-all border-2 ${
- currentPage === 1
- ? 'border-gray-200 bg-gray-50 text-gray-400 cursor-not-allowed'
- : 'border-blue-300 bg-white text-blue-700 hover:bg-blue-50'
- }">
- <i class="ri-arrow-left-s-line"></i> 上一页
- </button>
-
- <div class="flex items-center gap-1">
- ${Array.from({length: totalPages}, (_, i) => i + 1).map(page => {
- if (page === 1 || page === totalPages || (page >= currentPage - 2 && page <= currentPage + 2)) {
- return `
- <button
- onclick="loadPendingQuestionsByKpPage('${kpCode}', ${page})"
- class="px-3 py-2 rounded-lg text-sm font-medium transition-all border-2 ${
- page === currentPage
- ? 'border-blue-500 bg-blue-500 text-white'
- : 'border-gray-300 bg-white text-gray-700 hover:bg-gray-50'
- }">
- ${page}
- </button>
- `;
- } else if (page === currentPage - 3 || page === currentPage + 3) {
- return '<span class="px-2 text-gray-400">...</span>';
- }
- return '';
- }).join('')}
- </div>
-
- <button
- onclick="loadPendingQuestionsByKpPage('${kpCode}', ${currentPage + 1})"
- ${currentPage === totalPages ? 'disabled' : ''}
- class="px-4 py-2 rounded-lg text-sm font-medium transition-all border-2 ${
- currentPage === totalPages
- ? 'border-gray-200 bg-gray-50 text-gray-400 cursor-not-allowed'
- : 'border-blue-300 bg-white text-blue-700 hover:bg-blue-50'
- }">
- 下一页 <i class="ri-arrow-right-s-line"></i>
- </button>
- </div>
- </div>
- `;
- }
- }
-
- html += '</div>';
- container.innerHTML = html;
-
- // 渲染数学公式
- if (window.renderMathInElement) {
- container.querySelectorAll('.math-render').forEach(el => {
- try {
- window.renderMathInElement(el, {
- delimiters: [
- {left: "$$", right: "$$", display: true},
- {left: "$", right: "$", display: false},
- {left: "\\(", right: "\\)", display: false},
- {left: "\\[", right: "\\]", display: true}
- ],
- throwOnError: false
- });
- } catch (e) {
- console.warn('数学公式渲染失败:', e);
- }
- });
- }
- })
- .catch(error => {
- container.innerHTML = `
- <div class="apple-card p-12 text-center">
- <div class="text-red-400 mb-4">
- <i class="ri-error-warning-line text-6xl"></i>
- </div>
- <h3 class="text-lg font-bold text-gray-600 mb-2">加载失败</h3>
- <p class="text-sm text-red-500">${error.message || '未知错误'}</p>
- </div>
- `;
- });
- }
- // 分页加载函数(完全按照题目管理页面的格式)
- function loadPendingQuestionsByKpPage(kpCode, page) {
- // 更新URL参数,但不刷新页面
- const url = new URL(window.location);
- url.searchParams.set('kp_code', kpCode);
- if (page > 1) {
- url.searchParams.set('page', page);
- } else {
- url.searchParams.delete('page');
- }
- window.history.pushState({}, '', url);
-
- // 重新加载题目列表
- const linkElement = document.querySelector(`.kp-link[data-kp-code="${kpCode}"]`);
- const kpName = linkElement ? (linkElement.getAttribute('data-kp-name') || kpCode) : kpCode;
- loadPendingQuestionsByKp(kpCode, kpName, linkElement, page);
- }
- // 加载所有题目(根据筛选条件)
- function loadAllQuestionsByFilter(page = 1) {
- const container = document.getElementById('questions-container');
- if (!container) return;
-
- // 获取筛选参数
- const createByFilter = document.getElementById('filter-create-by')?.value || '';
- const createdAtFilter = document.getElementById('filter-created-at')?.value || '';
-
- // 如果没有筛选条件,不加载
- if (!createByFilter && !createdAtFilter) {
- // 如果有选择的知识点,加载该知识点;否则显示提示
- if (currentKpCode !== null) {
- const linkElement = document.querySelector(`.kp-link[data-kp-code="${currentKpCode}"]`);
- const kpName = linkElement ? (linkElement.getAttribute('data-kp-name') || currentKpCode) : currentKpCode;
- loadPendingQuestionsByKp(currentKpCode, kpName, linkElement, page);
- } else {
- container.innerHTML = `
- <div class="apple-card p-12 text-center">
- <div class="text-gray-400 mb-4">
- <i class="ri-file-check-line text-6xl"></i>
- </div>
- <h3 class="text-lg font-bold text-gray-600 mb-2">请选择左侧知识点或设置筛选条件</h3>
- <p class="text-sm text-gray-500 mb-6">点击知识点查看该知识点下的未审核题目,或使用筛选条件查看所有题目</p>
- </div>
- `;
- }
- return;
- }
-
- // 清除左侧选中状态
- document.querySelectorAll('.kp-link').forEach(link => {
- link.classList.remove('bg-orange-50', 'border-orange-400');
- });
-
- // 显示加载状态
- container.innerHTML = `
- <div class="apple-card p-12 text-center">
- <div class="text-orange-500 mb-4">
- <i class="ri-loader-4-line text-6xl animate-spin"></i>
- </div>
- <p class="text-gray-600">正在加载未审核题目...</p>
- </div>
- `;
-
- // 构建请求URL(使用'all'表示所有题目)
- let apiUrl = `/api/pending_questions_by_kp/all`;
- const params = new URLSearchParams();
- if (page > 1) {
- params.append('page', page);
- }
- if (createByFilter) {
- params.append('create_by', createByFilter);
- }
- if (createdAtFilter) {
- params.append('created_at', createdAtFilter);
- }
- if (params.toString()) {
- apiUrl += '?' + params.toString();
- }
-
- // 请求题目列表
- fetch(apiUrl)
- .then(response => response.json())
- .then(data => {
- if (!data.success) {
- throw new Error(data.error || '加载失败');
- }
-
- const questions = data.questions || [];
- const kpName = data.kp_name || '全部题目';
-
- // 获取分页信息
- const pagination = data.pagination || {};
- const currentPage = pagination.page || 1;
- const totalPages = pagination.total_pages || 1;
- const totalCount = pagination.total_count || questions.length;
- const pageSize = pagination.page_size || 20;
- const startIndex = (currentPage - 1) * pageSize;
- const endIndex = Math.min(startIndex + pageSize, totalCount);
-
- // 渲染题目列表(复用相同的渲染逻辑)
- let html = '';
-
- html += `<div class="space-y-3 mt-6">`;
-
- if (questions.length === 0) {
- html += `
- <div class="apple-card p-12 text-center">
- <div class="text-gray-400 mb-4">
- <i class="ri-file-list-line text-6xl"></i>
- </div>
- <h3 class="text-lg font-bold text-gray-600 mb-2">没有找到符合条件的题目</h3>
- <p class="text-sm text-gray-500">请尝试调整筛选条件</p>
- </div>
- `;
- } else {
- questions.forEach(q => {
- let auditBadge = '<span class="bg-orange-100 text-orange-700 px-2 py-0.5 rounded text-xs font-bold whitespace-nowrap">待审核</span>';
-
- let difficultyBadge = '';
- if (q.difficulty !== null && q.difficulty !== undefined) {
- const diff = parseFloat(q.difficulty);
- if (diff === 0.2 || Math.abs(diff - 0.2) < 0.1) {
- difficultyBadge = '<span class="px-2 py-0.5 rounded-full text-xs font-bold bg-green-100 text-green-700 border border-green-200 whitespace-nowrap">筑基</span>';
- } else if (diff === 0.4 || Math.abs(diff - 0.4) < 0.1) {
- difficultyBadge = '<span class="px-2 py-0.5 rounded-full text-xs font-bold bg-yellow-100 text-yellow-700 border border-yellow-200 whitespace-nowrap">提分</span>';
- } else if (diff === 0.7 || Math.abs(diff - 0.7) < 0.1) {
- difficultyBadge = '<span class="px-2 py-0.5 rounded-full text-xs font-bold bg-orange-100 text-orange-700 border border-orange-200 whitespace-nowrap">培优</span>';
- }
- }
-
- const typeMap = {'choice': '选择题', 'fill': '填空题', 'answer': '解答题'};
- const questionTypeText = typeMap[q.question_type] || q.question_type || '未分类';
-
- let gradeBadge = '';
- if (q.grade !== null && q.grade !== undefined) {
- const grade = parseInt(q.grade);
- if (grade === 1) {
- gradeBadge = '<span class="px-2 py-0.5 rounded-full text-xs font-bold bg-pink-100 text-pink-700 border border-pink-200 whitespace-nowrap">小学</span>';
- } else if (grade === 2) {
- gradeBadge = '<span class="px-2 py-0.5 rounded-full text-xs font-bold bg-blue-100 text-blue-700 border border-blue-200 whitespace-nowrap">初中</span>';
- } else if (grade === 3) {
- gradeBadge = '<span class="px-2 py-0.5 rounded-full text-xs font-bold bg-purple-100 text-purple-700 border border-purple-200 whitespace-nowrap">高中</span>';
- }
- }
-
- const stemText = (q.stem || '').replace(/<[^>]*>/g, '').substring(0, 150);
-
- html += `
- <a href="/detail/${q.question_code}"
- class="apple-card p-4 block group hover:shadow-lg transition-all border-l-4 border-transparent hover:border-blue-500">
- <div class="flex items-center gap-4">
- <div class="flex-shrink-0">
- <span class="text-xs font-mono text-gray-500 bg-gray-100 px-3 py-1.5 rounded font-semibold">${q.question_code}</span>
- </div>
- <div class="flex-1 min-w-0">
- <div class="text-sm text-gray-800 group-hover:text-blue-600 transition-colors line-clamp-1">
- ${stemText}${stemText.length >= 150 ? '...' : ''}
- </div>
- </div>
- <div class="flex items-center gap-3 flex-shrink-0">
- <span class="text-xs text-gray-500 bg-gray-50 px-2 py-1 rounded">${questionTypeText}</span>
- ${gradeBadge}
- ${difficultyBadge}
- ${auditBadge}
- <span class="text-xs text-blue-600 font-medium opacity-0 group-hover:opacity-100 transition-opacity whitespace-nowrap">查看详情 →</span>
- </div>
- </div>
- </a>
- `;
- });
-
- // 添加分页控件
- if (totalPages > 1) {
- html += `
- <div class="mt-6 flex items-center justify-between">
- <div class="text-sm text-gray-600">
- 显示第 ${startIndex + 1}-${endIndex} 题,共 ${totalCount} 题
- </div>
- <div class="flex items-center gap-2">
- <button
- onclick="loadAllQuestionsByFilterPage(${currentPage - 1})"
- ${currentPage === 1 ? 'disabled' : ''}
- class="px-4 py-2 rounded-lg text-sm font-medium transition-all border-2 ${
- currentPage === 1
- ? 'border-gray-200 bg-gray-50 text-gray-400 cursor-not-allowed'
- : 'border-blue-300 bg-white text-blue-700 hover:bg-blue-50'
- }">
- <i class="ri-arrow-left-s-line"></i> 上一页
- </button>
- <div class="flex items-center gap-1">
- ${Array.from({length: totalPages}, (_, i) => i + 1).map(p => {
- if (p === 1 || p === totalPages || (p >= currentPage - 2 && p <= currentPage + 2)) {
- return `
- <button
- onclick="loadAllQuestionsByFilterPage(${p})"
- class="px-3 py-2 rounded-lg text-sm font-medium transition-all border-2 ${
- p === currentPage
- ? 'border-blue-500 bg-blue-500 text-white'
- : 'border-gray-300 bg-white text-gray-700 hover:bg-gray-50'
- }">
- ${p}
- </button>
- `;
- } else if (p === currentPage - 3 || p === currentPage + 3) {
- return '<span class="px-2 text-gray-400">...</span>';
- }
- return '';
- }).join('')}
- </div>
- <button
- onclick="loadAllQuestionsByFilterPage(${currentPage + 1})"
- ${currentPage === totalPages ? 'disabled' : ''}
- class="px-4 py-2 rounded-lg text-sm font-medium transition-all border-2 ${
- currentPage === totalPages
- ? 'border-gray-200 bg-gray-50 text-gray-400 cursor-not-allowed'
- : 'border-blue-300 bg-white text-blue-700 hover:bg-blue-50'
- }">
- 下一页 <i class="ri-arrow-right-s-line"></i>
- </button>
- </div>
- </div>
- `;
- }
- }
-
- html += '</div>';
- container.innerHTML = html;
-
- // 渲染数学公式
- if (window.renderMathInElement) {
- container.querySelectorAll('.math-render').forEach(el => {
- try {
- window.renderMathInElement(el, {
- delimiters: [
- {left: "$$", right: "$$", display: true},
- {left: "$", right: "$", display: false},
- {left: "\\(", right: "\\)", display: false},
- {left: "\\[", right: "\\]", display: true}
- ],
- throwOnError: false
- });
- } catch (e) {
- console.warn('数学公式渲染失败:', e);
- }
- });
- }
- })
- .catch(error => {
- container.innerHTML = `
- <div class="apple-card p-12 text-center">
- <div class="text-red-400 mb-4">
- <i class="ri-error-warning-line text-6xl"></i>
- </div>
- <h3 class="text-lg font-bold text-gray-600 mb-2">加载失败</h3>
- <p class="text-sm text-red-500">${error.message || '未知错误'}</p>
- </div>
- `;
- });
- }
- // 分页加载所有题目(根据筛选条件)
- function loadAllQuestionsByFilterPage(page) {
- loadAllQuestionsByFilter(page);
- }
- // 重置筛选条件
- function resetFilters() {
- document.getElementById('filter-create-by').value = '';
- const dateInput = document.getElementById('filter-created-at');
- if (dateInput) {
- dateInput.value = '';
- }
- // 重新加载当前知识点的题目,如果没有选择知识点则显示提示
- if (currentKpCode !== null) {
- const linkElement = document.querySelector(`.kp-link[data-kp-code="${currentKpCode}"]`);
- const kpName = linkElement ? (linkElement.getAttribute('data-kp-name') || currentKpCode) : currentKpCode;
- loadPendingQuestionsByKp(currentKpCode, kpName, linkElement, 1);
- } else {
- const container = document.getElementById('questions-container');
- if (container) {
- container.innerHTML = `
- <div class="apple-card p-12 text-center">
- <div class="text-gray-400 mb-4">
- <i class="ri-file-check-line text-6xl"></i>
- </div>
- <h3 class="text-lg font-bold text-gray-600 mb-2">请选择左侧知识点或设置筛选条件</h3>
- <p class="text-sm text-gray-500 mb-6">点击知识点查看该知识点下的未审核题目,或使用筛选条件查看所有题目</p>
- </div>
- `;
- }
- }
- }
- // 监听筛选条件变化
- document.addEventListener('DOMContentLoaded', function() {
- const createByFilter = document.getElementById('filter-create-by');
- const createdAtFilter = document.getElementById('filter-created-at');
-
- // 检查是否有筛选条件的函数
- function checkAndLoad() {
- const createBy = createByFilter?.value || '';
- const createdAt = createdAtFilter?.value || '';
-
- // 如果有筛选条件,加载所有题目
- if (createBy || createdAt) {
- loadAllQuestionsByFilter(1);
- } else {
- // 如果没有筛选条件,加载当前知识点的题目
- if (currentKpCode !== null) {
- const linkElement = document.querySelector(`.kp-link[data-kp-code="${currentKpCode}"]`);
- const kpName = linkElement ? (linkElement.getAttribute('data-kp-name') || currentKpCode) : currentKpCode;
- loadPendingQuestionsByKp(currentKpCode, kpName, linkElement, 1);
- }
- }
- }
-
- if (createByFilter) {
- createByFilter.addEventListener('change', checkAndLoad);
- }
-
- if (createdAtFilter) {
- // 日期选择器支持 change 和 input 事件
- createdAtFilter.addEventListener('change', checkAndLoad);
- createdAtFilter.addEventListener('input', checkAndLoad);
- }
- });
- // 页面加载完成后,初始化知识点目录
- document.addEventListener('DOMContentLoaded', function() {
- // 默认展开第一级节点
- const firstLevelNodes = document.querySelectorAll('.kp-node-item[data-level="0"]');
- firstLevelNodes.forEach(node => {
- const kpCode = node.getAttribute('data-kp-code');
- const expandBtn = node.querySelector('.kp-expand-btn');
- if (expandBtn && expandBtn.getAttribute('data-expanded') === 'true') {
- const childrenContainer = document.getElementById(`kp-children-${kpCode}`);
- if (childrenContainer) {
- childrenContainer.classList.remove('hidden');
- }
- }
- });
- });
- </script>
- {% endblock %}
|