|
|
@@ -1,8 +1,286 @@
|
|
|
<template>
|
|
|
+ <div class="app-container">
|
|
|
+ <div class="flex flex-row justify-between items-center">
|
|
|
+ <el-button icon="back" @click="router.back()">返回</el-button>
|
|
|
+ <div class="flex-1 flex justify-center items-center">
|
|
|
+ <el-text>{{ paperLocal.batchName }}</el-text>
|
|
|
+ <el-input v-model="paperLocal.name" placeholder="输入试卷名称" class="ml-5" style="width: 220px"/>
|
|
|
+ </div>
|
|
|
+ <el-button type="primary" icon="select" @click="handleSavePaper">保存</el-button>
|
|
|
+ </div>
|
|
|
+ <el-divider/>
|
|
|
+ <el-card v-for="(group, idx) in groupedQuestions" shadow="never" class="mb-5">
|
|
|
+ <div :class="'drag_' + idx">
|
|
|
+ <div slot="header" class="clearfix" @mouseover="showScore(group, idx)"
|
|
|
+ @mouseout="hideScore(group, idx)" draggable="true"
|
|
|
+ @dragstart="handleDragStart($event, group)"
|
|
|
+ @dragover.prevent="handleDragOver($event, group)"
|
|
|
+ @drop="handleDragEnter($event, group)" @dragend="handleDragEnd($event, group)">
|
|
|
+ <el-text class="font-bold">
|
|
|
+ {{ numberToChinese(idx + 1) }}、{{ group.title }}(共{{ group.num }}题;共{{ group.score }}分)
|
|
|
+ </el-text>
|
|
|
+ <span style="float: right; display: none" :id="'parent_score_' + idx">
|
|
|
+ <el-button type="text" @click="openGroupScore=true,groupIdx=idx">批量设置得分</el-button>
|
|
|
+ <el-button type="text" @click="deleteType(idx)">删除分类</el-button>
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ <div v-for="(q, i) in group.list" class="mb-5"
|
|
|
+ @mouseover="showQuestionScore(idx, i)" @mouseout="hideQuestionScore(idx, i)"
|
|
|
+ @dragstart="handleQuestionDragStart($event, q, i)"
|
|
|
+ @dragover.prevent="handleQuestionDragOver($event, q)"
|
|
|
+ @drop="handleQuestionDragEnter($event, q, idx, i)"
|
|
|
+ @dragend="handleQuestionDragEnd($event, q)" draggable="true">
|
|
|
+ <div style="display: none" :id="'score_' + idx + '_' + i">
|
|
|
+ <el-button type="text" @click="openScore=true,groupIdx=idx,qIdx=i">
|
|
|
+ 设置得分
|
|
|
+ </el-button>
|
|
|
+ <el-button type="text" @click="showQuestionParse(q)">解析</el-button>
|
|
|
+ <el-button type="text" @click="deleteQuestion(idx, i)">删除题目</el-button>
|
|
|
+ </div>
|
|
|
+ <div>
|
|
|
+ {{ i + 1 }}.
|
|
|
+ <span style="color: #1890ff">题号:{{ q.id }}({{ q.score }}分)</span><span
|
|
|
+ v-html="q.title"></span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
|
|
|
+ <!-- 批量设置分数-->
|
|
|
+ <el-dialog v-model="openGroupScore" width="500px" append-to-body>
|
|
|
+ <el-form label-width="80px">
|
|
|
+ <el-form-item label="分数" prop="parentScore">
|
|
|
+ <el-input-number v-model.number="groupScore" :min="1" placeholder="请输入分数"/>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ <div slot="footer" class="dialog-footer">
|
|
|
+ <el-button type="primary" @click="setGroupScore">确 定</el-button>
|
|
|
+ <el-button @click="openGroupScore=false">取 消</el-button>
|
|
|
+ </div>
|
|
|
+ </el-dialog>
|
|
|
+ <!-- 设置题目分数 -->
|
|
|
+ <el-dialog v-model="openScore" width="500px" append-to-body>
|
|
|
+ <el-form label-width="80px">
|
|
|
+ <el-form-item label="分数" prop="parentScore">
|
|
|
+ <el-input-number v-model.number="qScore" :min="1" placeholder="请输入分数"/>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ <div slot="footer" class="dialog-footer">
|
|
|
+ <el-button type="primary" @click="setScore">确 定</el-button>
|
|
|
+ <el-button @click="openScore=false">取 消</el-button>
|
|
|
+ </div>
|
|
|
+ </el-dialog>
|
|
|
+ <!-- 设置题目解析 -->
|
|
|
+ <el-dialog v-model="openParse" title="解析" width="70%" append-to-body center>
|
|
|
+ <el-form label-width="80px" id="paperDialog">
|
|
|
+ <el-form-item label="解析">
|
|
|
+ <editor v-model="parseCopy.parse" :min-height="122"/>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="答案1">
|
|
|
+ <editor v-model="parseCopy.answer1" :min-height="122"/>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="答案2">
|
|
|
+ <editor v-model="parseCopy.answer2" :min-height="122"/>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ <div slot="footer" class="dialog-footer">
|
|
|
+ <el-button type="primary" @click="setQuestionParse">确 定</el-button>
|
|
|
+ <el-button @click="openParse=false">取 消</el-button>
|
|
|
+ </div>
|
|
|
+ </el-dialog>
|
|
|
+ </div>
|
|
|
</template>
|
|
|
|
|
|
<script setup name="PaperDetail">
|
|
|
+
|
|
|
+import {usePaperResolver} from "@/views/dz/papers/hooks/usePaperStorage.js";
|
|
|
+import router from "@/router/index.js";
|
|
|
+import {ElMessage} from "element-plus";
|
|
|
+import {buildPaperManual} from "@/api/dz/papers.js";
|
|
|
+
|
|
|
+const {paperLocal, groupedQuestions, toCommitPaper} = usePaperResolver()
|
|
|
+
|
|
|
+const chnNumChar = ["零", "一", "二", "三", "四", "五", "六", "七", "八", "九"];
|
|
|
+const chnUnitSection = ["", "万", "亿", "万亿", "亿亿"];
|
|
|
+const chnUnitChar = ["", "十", "百", "千"];
|
|
|
+
|
|
|
+const groupIdx = ref(0)
|
|
|
+const qIdx = ref(0)
|
|
|
+const openGroupScore = ref(false)
|
|
|
+const openScore = ref(false)
|
|
|
+const groupScore = ref(1)
|
|
|
+const qScore = ref(1)
|
|
|
+const openParse = ref(false)
|
|
|
+const parseQuestion = ref(null)
|
|
|
+const parseCopy = ref(null)
|
|
|
+
|
|
|
+const sectionToChinese = function (section) {
|
|
|
+ let strIns = "", chnStr = "";
|
|
|
+ let unitPos = 0;
|
|
|
+ let zero = true;
|
|
|
+ while (section > 0) {
|
|
|
+ const v = section % 10;
|
|
|
+ if (v === 0) {
|
|
|
+ if (!zero) {
|
|
|
+ zero = true;
|
|
|
+ chnStr = chnNumChar[v] + chnStr;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ zero = false;
|
|
|
+ strIns = chnNumChar[v];
|
|
|
+ strIns += chnUnitChar[unitPos];
|
|
|
+ chnStr = strIns + chnStr;
|
|
|
+ }
|
|
|
+ unitPos++;
|
|
|
+ section = Math.floor(section / 10);
|
|
|
+ }
|
|
|
+ return chnStr;
|
|
|
+}
|
|
|
+const numberToChinese = function (num) {
|
|
|
+ let unitPos = 0;
|
|
|
+ let strIns = "", chnStr = "";
|
|
|
+ let needZero = false;
|
|
|
+
|
|
|
+ if (num === 0) {
|
|
|
+ return chnNumChar[0];
|
|
|
+ }
|
|
|
+
|
|
|
+ while (num > 0) {
|
|
|
+ const section = num % 10000;
|
|
|
+ if (needZero) {
|
|
|
+ chnStr = chnNumChar[0] + chnStr;
|
|
|
+ }
|
|
|
+ strIns = sectionToChinese(section);
|
|
|
+ strIns +=
|
|
|
+ section !== 0 ? chnUnitSection[unitPos] : chnUnitSection[0];
|
|
|
+ chnStr = strIns + chnStr;
|
|
|
+ needZero = section < 1000 && section > 0;
|
|
|
+ num = Math.floor(num / 10000);
|
|
|
+ unitPos++;
|
|
|
+ }
|
|
|
+
|
|
|
+ return chnStr;
|
|
|
+}
|
|
|
+const showQuestionParse = function (q) {
|
|
|
+ openParse.value = true
|
|
|
+ parseQuestion.value = q
|
|
|
+ parseCopy.value = {...q}
|
|
|
+}
|
|
|
+const setQuestionParse = function () {
|
|
|
+ openParse.value = false
|
|
|
+ const {parse, answer1, answer2} = parseCopy.value
|
|
|
+ parseQuestion.value.parse = parse
|
|
|
+ parseQuestion.value.answer1 = answer1
|
|
|
+ parseQuestion.value.answer2 = answer2
|
|
|
+}
|
|
|
+const showScore = function (data, index) {
|
|
|
+ const doc = document.getElementById("parent_score_" + index);
|
|
|
+ doc.style.display = "block";
|
|
|
+}
|
|
|
+const hideScore = function (data, index) {
|
|
|
+ const doc = document.getElementById("parent_score_" + index);
|
|
|
+ doc.style.display = "none";
|
|
|
+}
|
|
|
+
|
|
|
+const setGroupScore = function () {
|
|
|
+ const group = groupedQuestions.value[groupIdx.value];
|
|
|
+ const len = group.list.length;
|
|
|
+ group.score = len * groupScore.value;
|
|
|
+ for (let q of group.list) {
|
|
|
+ q.score = groupScore.value;
|
|
|
+ }
|
|
|
+ openGroupScore.value = false;
|
|
|
+}
|
|
|
+
|
|
|
+const setScore = function () {
|
|
|
+ const group = groupedQuestions.value[groupIdx.value]
|
|
|
+ const q = group.list[qIdx.value]
|
|
|
+ q.score = qScore.value
|
|
|
+ group.score = group.list.reduce((a, b) => a + b.score, 0)
|
|
|
+ openScore.value = false
|
|
|
+}
|
|
|
+
|
|
|
+const showQuestionScore = function (gIdx, qIdx) {
|
|
|
+ const doc = document.getElementById("score_" + gIdx + "_" + qIdx);
|
|
|
+ doc.style.display = "block";
|
|
|
+}
|
|
|
+const hideQuestionScore = function (gIdx, qIdx) {
|
|
|
+ const doc = document.getElementById("score_" + gIdx + "_" + qIdx);
|
|
|
+ doc.style.display = "none";
|
|
|
+}
|
|
|
+const deleteType = function (gIdx) {
|
|
|
+ groupedQuestions.value.splice(gIdx, 1);
|
|
|
+}
|
|
|
+const deleteQuestion = function (gIdx, qIdx) {
|
|
|
+ const group = groupedQuestions.value[gIdx]
|
|
|
+ group.list.splice(qIdx, 1)
|
|
|
+ if (group.list.length) {
|
|
|
+ group.num = group.list.length
|
|
|
+ group.score = group.list.reduce((a, b) => a + b.score, 0)
|
|
|
+ } else {
|
|
|
+ groupedQuestions.value.splice(gIdx, 1)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 拖动组
|
|
|
+const draggingGroup = ref(null)
|
|
|
+const handleDragStart = function (e, group) {
|
|
|
+ draggingGroup.value = group
|
|
|
+}
|
|
|
+const handleDragEnd = function (e, group) {
|
|
|
+ draggingGroup.value = null;
|
|
|
+}
|
|
|
+const handleDragOver = function (e) {
|
|
|
+ e.dataTransfer.dropEffect = "move"; // e.dataTransfer.dropEffect="move";//在dragenter中针对放置目标来设置!
|
|
|
+}
|
|
|
+const handleDragEnter = function (e, group) {
|
|
|
+ e.dataTransfer.effectAllowed = "move"; //为需要移动的元素设置dragstart事件
|
|
|
+ if (group === draggingGroup.value) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const newItems = groupedQuestions.value;
|
|
|
+ const src = newItems.indexOf(draggingGroup.value);
|
|
|
+ const dst = newItems.indexOf(group);
|
|
|
+
|
|
|
+ newItems.splice(dst, 0, ...newItems.splice(src, 1))
|
|
|
+}
|
|
|
+// 拖动题
|
|
|
+const draggingQuestion = ref(null)
|
|
|
+const draggingQIdx = ref(0)
|
|
|
+const handleQuestionDragStart = function (e, q, i) {
|
|
|
+ draggingQuestion.value = q
|
|
|
+ draggingQIdx.value = i
|
|
|
+}
|
|
|
+const handleQuestionDragEnd = function (e, q) {
|
|
|
+ draggingQuestion.value = null
|
|
|
+ draggingQIdx.value = null
|
|
|
+}
|
|
|
+const handleQuestionDragOver = function (e) {
|
|
|
+ e.dataTransfer.dropEffect = "move"; // e.dataTransfer.dropEffect="move";//在dragenter中针对放置目标来设置!
|
|
|
+}
|
|
|
+const handleQuestionDragEnter = function (e, q, gIdx, qIdx) {
|
|
|
+ e.dataTransfer.effectAllowed = "move"; //为需要移动的元素设置dragstart事件
|
|
|
+ if (q === draggingQuestion.value && qIdx == draggingQIdx) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const newItems = groupedQuestions.value[gIdx].list;
|
|
|
+ const src = draggingQIdx.value;
|
|
|
+ const dst = qIdx;
|
|
|
+
|
|
|
+ newItems.splice(dst, 0, ...newItems.splice(src, 1));
|
|
|
+}
|
|
|
+
|
|
|
+const handleSavePaper = async function () {
|
|
|
+ const commit = toCommitPaper()
|
|
|
+ // validation
|
|
|
+ if (!commit.name) return ElMessage.error('请填写试卷名称')
|
|
|
+ if (!commit.questions?.length) return ElMessage.error('试卷至少包含1道试题')
|
|
|
+
|
|
|
+ // save
|
|
|
+ // TODO: 看看这个编辑有没有包含自动组卷的情况
|
|
|
+ await buildPaperManual(commit)
|
|
|
+ ElMessage.success('保存成功')
|
|
|
+}
|
|
|
</script>
|
|
|
|
|
|
<style scoped>
|