detail.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. <template>
  2. <div class="app-container">
  3. <div class="flex flex-row justify-between items-center">
  4. <el-button icon="back" @click="router.back()">返回</el-button>
  5. <div class="flex-1 flex justify-center items-center">
  6. <el-text>{{ paperLocal.batchName }}</el-text>
  7. <el-input v-model="paperLocal.name" placeholder="输入试卷名称" class="ml-5" style="width: 220px"/>
  8. </div>
  9. <el-button type="primary" icon="select" @click="handleSavePaper">保存</el-button>
  10. </div>
  11. <el-divider/>
  12. <el-card v-for="(group, idx) in groupedQuestions" shadow="never" class="mb-5">
  13. <div :class="'drag_' + idx">
  14. <div slot="header" class="clearfix" @mouseover="showScore(group, idx)"
  15. @mouseout="hideScore(group, idx)" draggable="true"
  16. @dragstart="handleDragStart($event, group)"
  17. @dragover.prevent="handleDragOver($event, group)"
  18. @drop="handleDragEnter($event, group)" @dragend="handleDragEnd($event, group)">
  19. <el-text class="font-bold">
  20. {{ numberToChinese(idx + 1) }}、{{ group.title }}(共{{ group.num }}题;共{{ group.score }}分)
  21. </el-text>
  22. <span style="float: right; display: none" :id="'parent_score_' + idx">
  23. <el-button type="text" @click="openGroupScore=true,groupIdx=idx">批量设置得分</el-button>
  24. <el-button type="text" @click="deleteType(idx)">删除分类</el-button>
  25. </span>
  26. </div>
  27. <div v-for="(q, i) in group.list" class="mb-5"
  28. @mouseover="showQuestionScore(idx, i)" @mouseout="hideQuestionScore(idx, i)"
  29. @dragstart="handleQuestionDragStart($event, q, i)"
  30. @dragover.prevent="handleQuestionDragOver($event, q)"
  31. @drop="handleQuestionDragEnter($event, q, idx, i)"
  32. @dragend="handleQuestionDragEnd($event, q)" draggable="true">
  33. <div style="display: none" :id="'score_' + idx + '_' + i">
  34. <el-button type="text" @click="openScore=true,groupIdx=idx,qIdx=i">
  35. 设置得分
  36. </el-button>
  37. <el-button type="text" @click="showQuestionParse(q)">解析</el-button>
  38. <el-button type="text" @click="deleteQuestion(idx, i)">删除题目</el-button>
  39. </div>
  40. <div>
  41. {{ i + 1 }}.
  42. <span style="color: #1890ff">题号:{{ q.id }}({{ q.score }}分)</span><span
  43. v-html="q.title"></span>
  44. </div>
  45. </div>
  46. </div>
  47. </el-card>
  48. <!-- 批量设置分数-->
  49. <el-dialog v-model="openGroupScore" width="500px" append-to-body>
  50. <el-form label-width="80px">
  51. <el-form-item label="分数" prop="parentScore">
  52. <el-input-number v-model.number="groupScore" :min="1" placeholder="请输入分数"/>
  53. </el-form-item>
  54. </el-form>
  55. <div slot="footer" class="dialog-footer">
  56. <el-button type="primary" @click="setGroupScore">确 定</el-button>
  57. <el-button @click="openGroupScore=false">取 消</el-button>
  58. </div>
  59. </el-dialog>
  60. <!-- 设置题目分数 -->
  61. <el-dialog v-model="openScore" width="500px" append-to-body>
  62. <el-form label-width="80px">
  63. <el-form-item label="分数" prop="parentScore">
  64. <el-input-number v-model.number="qScore" :min="1" placeholder="请输入分数"/>
  65. </el-form-item>
  66. </el-form>
  67. <div slot="footer" class="dialog-footer">
  68. <el-button type="primary" @click="setScore">确 定</el-button>
  69. <el-button @click="openScore=false">取 消</el-button>
  70. </div>
  71. </el-dialog>
  72. <!-- 设置题目解析 -->
  73. <el-dialog v-model="openParse" title="解析" width="70%" append-to-body center>
  74. <el-form label-width="80px" id="paperDialog">
  75. <el-form-item label="解析">
  76. <editor v-model="parseCopy.parse" :min-height="122"/>
  77. </el-form-item>
  78. <el-form-item label="答案1">
  79. <editor v-model="parseCopy.answer1" :min-height="122"/>
  80. </el-form-item>
  81. <el-form-item label="答案2">
  82. <editor v-model="parseCopy.answer2" :min-height="122"/>
  83. </el-form-item>
  84. </el-form>
  85. <div slot="footer" class="dialog-footer">
  86. <el-button type="primary" @click="setQuestionParse">确 定</el-button>
  87. <el-button @click="openParse=false">取 消</el-button>
  88. </div>
  89. </el-dialog>
  90. </div>
  91. </template>
  92. <script setup name="PaperDetail">
  93. import {usePaperResolver} from "@/views/dz/papers/hooks/usePaperStorage.js";
  94. import router from "@/router/index.js";
  95. import {ElMessage} from "element-plus";
  96. import {buildPaperManual} from "@/api/dz/papers.js";
  97. const {paperLocal, groupedQuestions, toCommitPaper} = usePaperResolver()
  98. const chnNumChar = ["零", "一", "二", "三", "四", "五", "六", "七", "八", "九"];
  99. const chnUnitSection = ["", "万", "亿", "万亿", "亿亿"];
  100. const chnUnitChar = ["", "十", "百", "千"];
  101. const groupIdx = ref(0)
  102. const qIdx = ref(0)
  103. const openGroupScore = ref(false)
  104. const openScore = ref(false)
  105. const groupScore = ref(1)
  106. const qScore = ref(1)
  107. const openParse = ref(false)
  108. const parseQuestion = ref(null)
  109. const parseCopy = ref(null)
  110. const sectionToChinese = function (section) {
  111. let strIns = "", chnStr = "";
  112. let unitPos = 0;
  113. let zero = true;
  114. while (section > 0) {
  115. const v = section % 10;
  116. if (v === 0) {
  117. if (!zero) {
  118. zero = true;
  119. chnStr = chnNumChar[v] + chnStr;
  120. }
  121. } else {
  122. zero = false;
  123. strIns = chnNumChar[v];
  124. strIns += chnUnitChar[unitPos];
  125. chnStr = strIns + chnStr;
  126. }
  127. unitPos++;
  128. section = Math.floor(section / 10);
  129. }
  130. return chnStr;
  131. }
  132. const numberToChinese = function (num) {
  133. let unitPos = 0;
  134. let strIns = "", chnStr = "";
  135. let needZero = false;
  136. if (num === 0) {
  137. return chnNumChar[0];
  138. }
  139. while (num > 0) {
  140. const section = num % 10000;
  141. if (needZero) {
  142. chnStr = chnNumChar[0] + chnStr;
  143. }
  144. strIns = sectionToChinese(section);
  145. strIns +=
  146. section !== 0 ? chnUnitSection[unitPos] : chnUnitSection[0];
  147. chnStr = strIns + chnStr;
  148. needZero = section < 1000 && section > 0;
  149. num = Math.floor(num / 10000);
  150. unitPos++;
  151. }
  152. return chnStr;
  153. }
  154. const showQuestionParse = function (q) {
  155. openParse.value = true
  156. parseQuestion.value = q
  157. parseCopy.value = {...q}
  158. }
  159. const setQuestionParse = function () {
  160. openParse.value = false
  161. const {parse, answer1, answer2} = parseCopy.value
  162. parseQuestion.value.parse = parse
  163. parseQuestion.value.answer1 = answer1
  164. parseQuestion.value.answer2 = answer2
  165. }
  166. const showScore = function (data, index) {
  167. const doc = document.getElementById("parent_score_" + index);
  168. doc.style.display = "block";
  169. }
  170. const hideScore = function (data, index) {
  171. const doc = document.getElementById("parent_score_" + index);
  172. doc.style.display = "none";
  173. }
  174. const setGroupScore = function () {
  175. const group = groupedQuestions.value[groupIdx.value];
  176. const len = group.list.length;
  177. group.score = len * groupScore.value;
  178. for (let q of group.list) {
  179. q.score = groupScore.value;
  180. }
  181. openGroupScore.value = false;
  182. }
  183. const setScore = function () {
  184. const group = groupedQuestions.value[groupIdx.value]
  185. const q = group.list[qIdx.value]
  186. q.score = qScore.value
  187. group.score = group.list.reduce((a, b) => a + b.score, 0)
  188. openScore.value = false
  189. }
  190. const showQuestionScore = function (gIdx, qIdx) {
  191. const doc = document.getElementById("score_" + gIdx + "_" + qIdx);
  192. doc.style.display = "block";
  193. }
  194. const hideQuestionScore = function (gIdx, qIdx) {
  195. const doc = document.getElementById("score_" + gIdx + "_" + qIdx);
  196. doc.style.display = "none";
  197. }
  198. const deleteType = function (gIdx) {
  199. groupedQuestions.value.splice(gIdx, 1);
  200. }
  201. const deleteQuestion = function (gIdx, qIdx) {
  202. const group = groupedQuestions.value[gIdx]
  203. group.list.splice(qIdx, 1)
  204. if (group.list.length) {
  205. group.num = group.list.length
  206. group.score = group.list.reduce((a, b) => a + b.score, 0)
  207. } else {
  208. groupedQuestions.value.splice(gIdx, 1)
  209. }
  210. }
  211. // 拖动组
  212. const draggingGroup = ref(null)
  213. const handleDragStart = function (e, group) {
  214. draggingGroup.value = group
  215. }
  216. const handleDragEnd = function (e, group) {
  217. draggingGroup.value = null;
  218. }
  219. const handleDragOver = function (e) {
  220. e.dataTransfer.dropEffect = "move"; // e.dataTransfer.dropEffect="move";//在dragenter中针对放置目标来设置!
  221. }
  222. const handleDragEnter = function (e, group) {
  223. e.dataTransfer.effectAllowed = "move"; //为需要移动的元素设置dragstart事件
  224. if (group === draggingGroup.value) {
  225. return;
  226. }
  227. const newItems = groupedQuestions.value;
  228. const src = newItems.indexOf(draggingGroup.value);
  229. const dst = newItems.indexOf(group);
  230. newItems.splice(dst, 0, ...newItems.splice(src, 1))
  231. }
  232. // 拖动题
  233. const draggingQuestion = ref(null)
  234. const draggingQIdx = ref(0)
  235. const handleQuestionDragStart = function (e, q, i) {
  236. draggingQuestion.value = q
  237. draggingQIdx.value = i
  238. }
  239. const handleQuestionDragEnd = function (e, q) {
  240. draggingQuestion.value = null
  241. draggingQIdx.value = null
  242. }
  243. const handleQuestionDragOver = function (e) {
  244. e.dataTransfer.dropEffect = "move"; // e.dataTransfer.dropEffect="move";//在dragenter中针对放置目标来设置!
  245. }
  246. const handleQuestionDragEnter = function (e, q, gIdx, qIdx) {
  247. e.dataTransfer.effectAllowed = "move"; //为需要移动的元素设置dragstart事件
  248. if (q === draggingQuestion.value && qIdx == draggingQIdx) {
  249. return;
  250. }
  251. const newItems = groupedQuestions.value[gIdx].list;
  252. const src = draggingQIdx.value;
  253. const dst = qIdx;
  254. newItems.splice(dst, 0, ...newItems.splice(src, 1));
  255. }
  256. const handleSavePaper = async function () {
  257. const commit = toCommitPaper()
  258. // validation
  259. if (!commit.name) return ElMessage.error('请填写试卷名称')
  260. if (!commit.questions?.length) return ElMessage.error('试卷至少包含1道试题')
  261. // save
  262. // TODO: 看看这个编辑有没有包含自动组卷的情况
  263. await buildPaperManual(commit)
  264. ElMessage.success('保存成功')
  265. }
  266. </script>
  267. <style scoped>
  268. </style>