PrimaryQuestionCard.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393
  1. <template>
  2. <el-card v-if="questionVO!==undefined" class="primary-question-card" shadow="hover">
  3. <template #header>
  4. <!-- <el-space>-->
  5. <el-space style="display: flex">
  6. <span><strong>S/N: </strong>{{ curNum + 1 }} / {{ totalNum }}</span>
  7. <span style="margin-left: 20px"><strong>题目类型: </strong>{{ questionVO.type }}</span>
  8. <span style="margin-left: 20px"><strong>知识点: </strong></span>
  9. <div>
  10. <!--知识点插槽,用于更新题目卡片中可能存在的知识点改变差异-->
  11. <slot name="knowledgeId">
  12. <!--知识点显示内容-->
  13. <el-tooltip
  14. v-if="questionVO.knowledgeId && questionVO.knowledgeId.length !== 0"
  15. v-for="item in questionVO.knowledgeId"
  16. effect="dark"
  17. placement="bottom">
  18. <template #content>
  19. {{ map(item) }}
  20. </template>
  21. <el-tag effect="dark" style="margin-right: 2px">{{ item }}</el-tag>
  22. </el-tooltip>
  23. <span v-else>null</span>
  24. </slot>
  25. </div>
  26. <el-button type="text" circle @click="isAllShow=!isAllShow"><strong>{{ isAllShowText }}</strong></el-button>
  27. <slot name="button">
  28. <!--按钮插槽,用于不同的卡片更换不同的按钮-->
  29. <el-button type="primary" icon="el-icon-edit" circle @click="updateQuestion"/>
  30. <el-button v-if="isDelete === false" type="danger" icon="el-icon-delete" circle @click="deleteQuestion"/>
  31. <el-button v-else type="success" icon="el-icon-delete" circle @click="recoverQuestion"/>
  32. </slot>
  33. </el-space>
  34. <!-- </el-space>-->
  35. </template>
  36. <!--题目内容部分-->
  37. <div class="primary-question-card-body">
  38. <!--题干部分-->
  39. <!--题干-题目描述部分-->
  40. <slot name="stem">
  41. <MarkdownText :raw="'<strong>题目: </strong>'+questionVO.stem"></MarkdownText>
  42. </slot>
  43. <!--题干-题目答案部分-->
  44. <div v-show="isAllShow">
  45. <!--单选题和多选题答案-->
  46. <slot name="choiceOption"
  47. v-if="questionVO.type==='choice' || questionVO.type==='multiple_choice'">
  48. <choice-option :question-v-o="questionVO"/>
  49. </slot>
  50. <!--todo:其他若干答案显示组件替代这里的div-->
  51. <div class="bQuestion-card-body-blanks" v-if="questionVO.type === 'blank'">
  52. <div v-for="index in Object.keys(questionVO.blanks).length">
  53. <div class="correct_answer">
  54. __{{ index }}__: {{ questionVO.blanks['__' + index + '__'] }}
  55. </div>
  56. </div>
  57. </div>
  58. <div class="bQuestion-card-body-true_false" v-if="questionVO.type === 'true_false'">
  59. <div class="correct_answer"> {{ questionVO.answer }}</div>
  60. </div>
  61. </div>
  62. </div>
  63. <el-divider v-show="isAllShow"></el-divider>
  64. <!--题目解析部分-->
  65. <div class="primary-question-card-analysis" v-show="isAllShow">
  66. <!--题目解析-题目信息部分-->
  67. <div class="analysis-block">
  68. <el-row>
  69. <slot name="keypoint">
  70. <el-col :span="6" style="text-align: left">
  71. <strong>关键词: </strong>{{ questionVO.keyPoints ? questionVO.keyPoints : '无' }}
  72. </el-col>
  73. </slot>
  74. <slot name="difficulty">
  75. <el-col :span="6" style="text-align: left">
  76. <strong>题目难度: </strong>{{ questionVO.weight }}/5
  77. </el-col>
  78. </slot>
  79. <slot name="tag">
  80. <el-col :span="12" style="text-align: left">
  81. <span><strong>标签: </strong></span>
  82. <span v-if="questionVO.tags && questionVO.tags.length !== 0">
  83. <el-tag v-for="(item, index) in questionVO.tags" effect="dark">
  84. {{ item }}
  85. </el-tag>
  86. </span>
  87. <span v-else>
  88. <span><el-tag effect="dark">无</el-tag></span>
  89. </span>
  90. </el-col>
  91. </slot>
  92. </el-row>
  93. </div>
  94. <!--题目解析-正确答案-->
  95. <div class="analysis-block" style="text-align: left">
  96. <!--单选、多选、判断、简单-->
  97. <slot name="answer1"
  98. v-if="questionVO.type === 'choice' ||
  99. questionVO.type === 'multiple_choice' ||
  100. questionVO.type === 'true_false' ||
  101. questionVO.type === 'short_answer'">
  102. <MarkdownText :raw="'<strong>正确答案: </strong>'+questionVO.answer"></MarkdownText>
  103. </slot>
  104. <!--todo: 简答-->
  105. </div>
  106. <!--题目解析-解析部分-->
  107. <div class="analysis-block" style="text-align: left">
  108. <!--单选、多选、判断-->
  109. <slot name="analysis1"
  110. v-if="questionVO.type === 'choice' ||
  111. questionVO.type === 'multiple_choice' ||
  112. questionVO.type === 'true_false'">
  113. <MarkdownText :raw="'<strong>解析: </strong>'+questionVO.analysis"></MarkdownText>
  114. </slot>
  115. <!--填空-->
  116. <div v-if="questionVO.type === 'blank' && questionVO.analysis.length > 5">
  117. <MarkdownText :raw="'<strong>解析: </strong>'+questionVO.analysis"></MarkdownText>
  118. </div>
  119. <!--简答-->
  120. <div v-if="questionVO.type === 'short_answer' && questionVO.analysis.length > 5">
  121. <MarkdownText :raw="'<strong>解析: </strong>'+questionVO.analysis"></MarkdownText>
  122. </div>
  123. </div>
  124. </div>
  125. <el-divider v-show="isAllShow"></el-divider>
  126. <!--题目操作记录部分-->
  127. <div class="primary-question-card-record" v-show="isAllShow">
  128. <!--题目当前状态-->
  129. <div class="record-block">
  130. <el-row>
  131. <el-col :span="3">
  132. <div><strong>状态: </strong>{{ questionVO.state }}</div>
  133. </el-col>
  134. <el-col :span="5">
  135. <div><strong>审核人: </strong>{{ checkedUserName }}</div>
  136. </el-col>
  137. </el-row>
  138. </div>
  139. <!--题目历史操作记录-->
  140. <div class="record-block" v-if="recordList.length !== 0">
  141. <el-table :data="recordTable" style="width: 100%;background-color: transparent" height="100">
  142. <el-table-column prop="序号" label="序号" width="240"/>
  143. <el-table-column prop="操作时间" label="操作时间" width="320"/>
  144. <el-table-column prop="操作人" label="操作人" width="240"/>
  145. <el-table-column prop="操作类型" label="操作类型" width="240"/>
  146. <el-table-column prop="备注" label="备注"/>
  147. </el-table>
  148. </div>
  149. <div class="record-block" v-else>
  150. <p>No Record!</p>
  151. </div>
  152. </div>
  153. </el-card>
  154. <!-- <el-drawer-->
  155. <!-- title="更新题目"-->
  156. <!-- :before-close="handleClose"-->
  157. <!-- v-model="dialog"-->
  158. <!-- direction="rtl"-->
  159. <!-- custom-class="question-drawer"-->
  160. <!-- ref="drawer"-->
  161. <!-- size="35%"-->
  162. <!-- >-->
  163. <!-- <QuestionUpdater :question="questionVO" @closeDialog="closeDialog"/>-->
  164. <!-- </el-drawer>-->
  165. </template>
  166. <script>
  167. import MarkdownText from "@/components/MarkdownText";
  168. import {map} from "@/utils/knowledgeMapper";
  169. import {toMyDate} from "@/utils/dateConvertor";
  170. import QuestionUpdater from "@/components/QuestionUpdater";
  171. import store from "@/store";
  172. import ChoiceOption from "@/components/QuestionCard/AnswerDisplay/ChoiceDisplay/index";
  173. import {ElDivider} from "element-plus";
  174. import {h, resolveComponent} from "vue";
  175. export default {
  176. name: "PrimaryQuestionCard",
  177. components: {
  178. MarkdownText,
  179. QuestionUpdater,
  180. ChoiceOption,
  181. },
  182. props: {
  183. questionVO: Object,
  184. curNum: Number,
  185. totalNum: Number,
  186. },
  187. data() {
  188. return {
  189. isAllShow: false,
  190. isAllRecord: false,
  191. isDelete: false,
  192. dialog: false,
  193. checkedUserName: '',
  194. recordList: [],
  195. recordNameDict: {},
  196. spacer: h(ElDivider, {direction: 'vertical'})
  197. }
  198. },
  199. methods: {
  200. toMyDate,
  201. map,
  202. expand() {
  203. if (!this.isAllShow) {
  204. this.isAllShow = true;
  205. }
  206. },
  207. withdraw() {
  208. if (this.isAllShow) {
  209. this.isAllShow = false;
  210. }
  211. },
  212. updateQuestion() {
  213. this.$emit("update");
  214. },
  215. handleClose() {
  216. this.$confirm('此操作将丢失填写信息, 是否继续?', '提示', {
  217. confirmButtonText: '确定',
  218. cancelButtonText: '取消',
  219. type: 'warning'
  220. }).then(() => {
  221. this.dialog = false;
  222. })
  223. },
  224. closeDialog() {
  225. this.dialog = false;
  226. },
  227. deleteQuestion() {
  228. this.$confirm('此操作将删除该题目, 是否继续?', '提示', {
  229. confirmButtonText: '确定',
  230. cancelButtonText: '取消',
  231. type: 'warning'
  232. }).then(() => {
  233. this.questionVO.lastModifiedTime = new Date();
  234. this.questionVO.state = "DELETING";
  235. let record = {
  236. userId: store.state.user.id,
  237. operationType: "Delete",
  238. }
  239. this.$api.updateQuestion(this.questionVO)
  240. .then(res => {
  241. this.$message({
  242. type: 'success',
  243. message: '删除成功!'
  244. });
  245. this.isDelete = true;
  246. record.questionId = res.data.result.id;
  247. record.time = new Date();
  248. this.$api.createRecord(record)
  249. .then(res1 => {
  250. console.log(res1);
  251. }).catch(err1 => {
  252. console.log(err1);
  253. })
  254. }).catch(error => {
  255. console.log(error);
  256. this.$notify.error({
  257. title: '错误',
  258. message: '删除失败'
  259. });
  260. })
  261. }).catch(() => {
  262. this.$message({
  263. type: 'info',
  264. message: '已取消删除'
  265. });
  266. });
  267. },
  268. recoverQuestion() {
  269. alert("Delete successfully!");
  270. },
  271. },
  272. computed: {
  273. reversedRecordList() {
  274. return this.recordList.reverse();
  275. },
  276. recordTable() {
  277. let recordTable1 = [];
  278. let length = this.recordList.length;
  279. for (let i = 0; i < length; i++) {
  280. let recordDict = {};
  281. recordDict["序号"] = i + 1;
  282. recordDict["操作时间"] = this.toMyDate(this.recordList[length - i - 1].time);
  283. recordDict["操作人"] = this.recordNameDict[this.recordList[length - i - 1].userId];
  284. recordDict["操作类型"] = this.recordList[length - i - 1].operationType;
  285. recordDict["备注"] = this.recordList[length - i - 1].remark;
  286. recordTable1.push(recordDict);
  287. }
  288. return recordTable1;
  289. },
  290. isAllShowText() {
  291. return this.isAllShow ? "收起" : "展开";
  292. },
  293. },
  294. mounted() {
  295. if (this.questionVO.checkedUserId) {
  296. this.$api.getUserById(this.questionVO.checkedUserId) // 异步函数1:根据用户ID获得用户名称
  297. .then(res => {
  298. if (res.data.result.name) {
  299. this.checkedUserName = res.data.result.name;
  300. }
  301. }).catch(err => {
  302. console.log(err);
  303. })
  304. }
  305. this.$api.getRecordByQid(this.questionVO.id) // 异步函数2:根据题目id获得ope_record
  306. .then(res1 => {
  307. let tempList = res1.data.result.content;
  308. this.recordList = tempList;
  309. for (let record of tempList) { //这里换成this.recordList是没问题的
  310. if (!this.recordNameDict[record.userId]) {
  311. this.$api.getUserById(record.userId) //异步函数3:根据记录中的用户ID获得其name
  312. .then(res2 => {
  313. if (res2.data.result.name) {
  314. this.recordNameDict[record.userId] = res2.data.result.name;
  315. }
  316. }).catch(err2 => {
  317. console.log(err2);
  318. })
  319. }
  320. }
  321. }).catch(err1 => {
  322. console.log(err1);
  323. });
  324. },
  325. emits: ["update"],
  326. }
  327. </script>
  328. <style>
  329. .primary-question-card {
  330. margin: 20px;
  331. font-size: 18px;
  332. background-color: #eef7f2;
  333. }
  334. .primary-question-card-body {
  335. text-align: left;
  336. /*margin-left: 32px;*/
  337. font-size: 20px;
  338. font-family: "Songti SC";
  339. }
  340. p {
  341. display: block;
  342. margin-block-start: 0px;
  343. margin-block-end: 0px;
  344. margin-inline-start: 0px;
  345. margin-inline-end: 0px;
  346. }
  347. .bQuestion-card-body-options {
  348. margin-top: 10px;
  349. font-size: 18px;
  350. }
  351. .underline-correct-answer {
  352. font-family: "Kaiti SC";
  353. font-size: 22px;
  354. color: #f1939c;
  355. font-weight: bold;
  356. text-decoration: underline;
  357. }
  358. .correct_answer {
  359. font-family: "Kaiti SC";
  360. font-size: 21px;
  361. color: #f1939c;
  362. font-weight: bold;
  363. }
  364. .analysis-block {
  365. /*margin-bottom: 10px;*/
  366. }
  367. .fail-check-option {
  368. padding: 5px;
  369. width: 100%;
  370. }
  371. .record-block {
  372. margin-bottom: 10px;
  373. }
  374. </style>