import { useEffect, useState } from "react"; import { Button, Descriptions, Empty, Form, Input, InputNumber, Modal, Radio, Select, Space, Table, message, } from "antd"; import { DownloadOutlined, PlusOutlined } from "@ant-design/icons"; import TableAction from "@cqsjjb/jjb-react-admin-component/TableAction"; import { Connect } from "@cqsjjb/jjb-dva-runtime"; import { NS_COURSEWARE, NS_PAPER } from "~/enumerate/namespace"; import { CLASS_PAPER_TYPE_MAP, CLASS_PAPER_TYPE_OPTIONS, QUESTION_TYPE_MAP, QUESTION_TYPE_OPTIONS, } from "~/enumerate/constant"; const API_HOST = window.process?.env?.app?.API_HOST || ""; /** 下载试卷 PDF(文件流,文件名取自响应头) */ const downloadPaperPdf = async (paperId) => { const res = await fetch( `${API_HOST}/safetyEval/paper/paper/exam/download?paperId=${paperId}`, { headers: { token: sessionStorage.getItem("token") || "" } }, ); const contentType = res.headers.get("content-type") || ""; if (contentType.includes("application/json")) { const json = await res.json(); throw new Error(json.errMessage || json.message || "下载失败"); } if (!res.ok) throw new Error(`下载失败(${res.status})`); const blob = await res.blob(); let fileName = "试卷.pdf"; const disposition = res.headers.get("content-disposition") || ""; const matched = disposition.match(/filename\*?=(?:UTF-8''|")?([^";]+)/i); if (matched?.[1]) { try { fileName = decodeURIComponent(matched[1].replace(/"/g, "")); } catch { fileName = matched[1].replace(/"/g, ""); } } const blobUrl = window.URL.createObjectURL(blob); const link = document.createElement("a"); link.href = blobUrl; link.download = fileName; link.style.display = "none"; document.body.appendChild(link); link.click(); document.body.removeChild(link); window.URL.revokeObjectURL(blobUrl); }; /** 班级课程 Tab — 配置试卷 / 试卷详情入口 */ function ClassPaperConfig(props) { const { classId } = props; /** 班级已绑定的试卷 id(来自班级详情) */ const [paperId, setPaperId] = useState(""); const [configOpen, setConfigOpen] = useState(false); /** 试卷详情/预览弹窗展示的试卷 id */ const [previewPaperId, setPreviewPaperId] = useState(""); /** 查询班级详情,取绑定试卷 */ const loadClassPaper = async () => { const res = await props.classDetail({ id: classId }); if (res?.success !== false) setPaperId(res?.data?.paperId || ""); }; useEffect(() => { if (classId) loadClassPaper(); }, [classId]); return ( <> setPreviewPaperId(id)} onBound={loadClassPaper} resetModelState={props.resetModelState} onClose={() => setConfigOpen(false)} /> setPreviewPaperId("")} /> ); } /** 配置试卷弹窗:已绑定时展示试卷信息(查看/编辑/下载),未绑定时提供新增 */ function PaperConfigModal({ open, classId, paperId, paper, courseware, basicLoading, fetchBasicInfo, paperPage, paperRuleGenerate, coursewarePage, classBindPaper, onPreview, onBound, resetModelState, onClose, }) { const [basicInfo, setBasicInfo] = useState(null); const [addOpen, setAddOpen] = useState(false); // 打开且有绑定试卷时才查询基本信息;paperId 变化(换绑)后重新查询 useEffect(() => { if (!open) { setAddOpen(false); resetModelState?.(NS_PAPER, { paperBasicLoading: false }); return; } setBasicInfo(null); if (paperId) { fetchBasicInfo({ paperId }).then((res) => { if (res?.success !== false) setBasicInfo(res?.data || null); }); } }, [open, paperId]); const columns = [ { title: "试卷类型", dataIndex: "paperType", width: 140, render: (v) => CLASS_PAPER_TYPE_MAP[v] ?? (v || "-"), }, { title: "考试时间(分钟)", dataIndex: "examTime", width: 140, render: (v) => (v == null ? "-" : v), }, { title: "操作", render: () => ( ), }, ]; return ( {!paperId && ( )} ), }} /> setAddOpen(false)} onSuccess={() => { setAddOpen(false); onBound(); }} /> ); } /** 新增/编辑试卷弹窗:平台试卷列表选用 或 自动生成试卷(规则表单) */ function PaperAddModal({ open, mode, classId, paper, courseware, paperPage, paperRuleGenerate, coursewarePage, classBindPaper, onPreview, resetModelState, onCancel, onSuccess, }) { const [form] = Form.useForm(); const [paperType, setPaperType] = useState("fixed"); const [query, setQuery] = useState({ current: 1, size: 10 }); const [keyword, setKeyword] = useState(""); const isFixed = paperType === "fixed"; const coursewareOptions = (courseware?.coursewareList || []).map((c) => ({ label: c.coursewareName, value: c.id, })); // 切换类型/打开时加载数据:平台试卷查分页,自动生成查课件下拉 useEffect(() => { if (!open) { resetModelState?.(NS_PAPER, { paperLoading: false, paperRuleLoading: false, }); resetModelState?.(NS_COURSEWARE, { classBindPaperLoading: false }); return; } // 规则表单挂载后才可重置,避免 useForm 未关联告警 if (!isFixed) form.resetFields(); setKeyword(""); if (isFixed) { setQuery({ current: 1, size: 10 }); paperPage({ current: 1, size: 10, paperType: "fixed" }); } else { coursewarePage({ current: 1, size: 200 }); } }, [open, paperType]); const handleSearch = (params) => { setQuery(params); paperPage({ paperType: "fixed", ...params }); }; /** 使用平台试卷:绑定到班级 */ const handleUse = async (record) => { const res = await classBindPaper({ id: classId, paperId: record.id, paperType: "fixed", examMinutes: record.examTime, }); if (res?.success === false) return; message.success("绑定成功"); onSuccess(); }; /** 自动生成试卷并绑定 */ const handleRuleOk = async () => { try { const values = await form.validateFields(); const res = await paperRuleGenerate(values); if (res?.success === false) return; const basic = res?.data || {}; const bindRes = await classBindPaper({ id: classId, paperId: basic.id, paperType: "config_rule", examMinutes: basic.examTime, }); if (bindRes?.success === false) return; message.success("自动生成试卷并绑定成功"); onSuccess(); } catch { // 表单校验失败,由 antd 提示 } }; const columns = [ { title: "试卷名称", dataIndex: "paperName", ellipsis: true }, { title: "试卷分数", dataIndex: "paperTotalScore", width: 90 }, { title: "合格分数", dataIndex: "paperPassScore", width: 90 }, { title: "考试时长(分)", dataIndex: "examTime", width: 110, render: (v) => (v == null ? "-" : v), }, { title: "操作", width: 130, render: (_, record) => ( ), }, ]; return ( setPaperType(e.target.value)} options={CLASS_PAPER_TYPE_OPTIONS} optionType="button" /> {isFixed ? ( <> setKeyword(e.target.value)} onSearch={(value) => handleSearch({ current: 1, size: query.size, paperName: value, }) } />
`共 ${t} 条`, current: Number(query.current) || 1, pageSize: Number(query.size) || 10, onChange: (page, pageSize) => handleSearch({ current: page, size: pageSize, paperName: query.paperName, }), }} /> ) : ( ))} )} )} ); } /** 试卷考试信息预览弹窗:基本信息 + 试题列表(试卷详情/查看/预览共用) */ function PaperExamPreviewModal({ open, paperId, title, loading, fetchExamInfo, resetModelState, onClose, }) { const [info, setInfo] = useState(null); useEffect(() => { if (!open) { resetModelState?.(NS_PAPER, { paperExamLoading: false }); return; } if (!paperId) return; setInfo(null); fetchExamInfo({ paperId }).then((res) => { if (res?.success !== false) setInfo(res?.data || null); }); }, [open, paperId]); const basic = info?.paperBasicInfoVO || {}; const columns = [ { title: "序号", width: 60, render: (_, __, index) => index + 1, }, { title: "题型", dataIndex: "questionType", width: 70, render: (v) => QUESTION_TYPE_MAP[v] ?? (v || "-"), }, { title: "题干", dataIndex: "title", render: (v, record) => ( <>
{v || "-"}
{(record.questionOptionVOList || []).map((o) => (
{o.optionKey}. {o.optionText}
))} ), }, { title: "分值", dataIndex: "score", width: 70 }, { title: "答案", dataIndex: "answer", width: 90 }, ]; return ( {basic.paperName || "-"} {CLASS_PAPER_TYPE_MAP[basic.paperType] ?? (basic.paperType || "-")} {basic.paperTotalScore ?? "-"} {basic.paperPassScore ?? "-"} {basic.examTime ?? "-"}
); } export default Connect([NS_COURSEWARE, NS_PAPER], true)(ClassPaperConfig);