paper-by-hand.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  1. <template>
  2. <div>
  3. <el-card>
  4. <mx-condition :local-data="extraData" :query-params="queryParams" :require-fields="requireFields"
  5. @query="handleQuery" use-alias-mapping></mx-condition>
  6. </el-card>
  7. <el-container>
  8. <left-tree-side>
  9. <span slot="title">{{ !isKnowledgeBranch ? '章节目录' : '知识点目录' }}</span>
  10. <el-tree ref="branchTree" highlight-current :expand-on-click-node="false" :data="branchTree"
  11. :props="treeProps" node-key="id" @node-click="handleTreeNodeClick"></el-tree>
  12. </left-tree-side>
  13. <!-- 手动组卷 -->
  14. <el-main style="background: #fff">
  15. <!-- 头部 -->
  16. <div class="main_header">
  17. <div class="left">
  18. <div class="main_tit">{{ title }}</div>
  19. <!-- 暂不支持 -->
  20. <div class="filter" style="display: flex; align-items: center" v-if="false">
  21. <input type="checkbox"/><span>过滤已做题</span>
  22. </div>
  23. </div>
  24. <div class="search_box">
  25. <input v-model="searchTerm" placeholder="请输入内容"/>
  26. <img src="@/assets/images/icon_search2.png" alt="" @click="resetQuestionQuery"/>
  27. </div>
  28. </div>
  29. <!-- 主题内容 -->
  30. <div class="main_con">
  31. <!-- 题目 -->
  32. <div class="paper_questions" :id="mathId">
  33. <div class="que_item" v-for="(item, index) in queList" :key="item.id">
  34. <div class="que_content">
  35. <div class="que-content-title">
  36. <div class="index">{{ (pageSize * (pageNum - 1) + index + 1) }}.</div>
  37. <div v-html="item.title"></div>
  38. </div>
  39. <div class="que-option">
  40. <div v-if="item.optionA">
  41. <span>A</span>
  42. <span v-html="item.optionA"></span>
  43. </div>
  44. <div v-if="item.optionB">
  45. <span>B</span>
  46. <span v-html="item.optionB"></span>
  47. </div>
  48. <div v-if="item.optionC">
  49. <span>C</span>
  50. <span v-html="item.optionC"></span>
  51. </div>
  52. <div v-if="item.optionD">
  53. <span>D</span>
  54. <span v-html="item.optionD"></span>
  55. </div>
  56. </div>
  57. </div>
  58. <div class="que_footer pd20 fx-row ai-center jc-between">
  59. <div class="spans">
  60. <span class="id">ID: {{ item.id }}</span>
  61. <span>题型: {{ item.qtpye }}</span>
  62. <span>难度: 一般</span>
  63. </div>
  64. <div class="operation">
  65. <div class="shoucan">
  66. <div v-show="item.collect" @click="toCancelCollectQue(item)"
  67. style="display: flex; align-items: center">
  68. <img src="@/assets/images/icon_shoucang_s.png" alt="" style="margin-right: 8px"/>
  69. <span>已收藏</span>
  70. </div>
  71. <div v-show="!item.collect" @click="toCollectQue(item)" style="display: flex; align-items: center">
  72. <img src="@/assets/images/icon_shoucang_n.png" style="margin-right: 8px" alt=""/>
  73. <span>收藏</span>
  74. </div>
  75. </div>
  76. <div class="jiucuo" @click="$refs.correct.open(item.id)">
  77. <img src="@/assets/images/icon_jiucuo.png" alt=""/>
  78. <span>纠错</span>
  79. </div>
  80. <div class="detail" @click="viewDetail(item)">
  81. <img src="@/assets/images/icon_chakan.png" alt=""/>
  82. <span>查看详情>></span>
  83. </div>
  84. <div class="addQue">
  85. <el-button @click="addQueCard(item)" icon="el-icon-shopping-cart-2" type="success" plain
  86. :disabled="queCardForm.queList.some((q) => q.id == item.id)">加入选题
  87. </el-button>
  88. </div>
  89. </div>
  90. </div>
  91. <div class="pd20" v-show="item.expand" v-html="'【解答】'+item.answer2"></div>
  92. <div class="pd20" v-show="item.expand" v-html="'【解析】'+item.parse"></div>
  93. </div>
  94. <div v-if="queList.length == 0" class="empty-text">
  95. 暂时没有内容
  96. </div>
  97. </div>
  98. </div>
  99. <pagination v-if="total > 0" :total="total" :page.sync="pageNum" :limit.sync="pageSize"
  100. @pagination="queryQuestions"/>
  101. </el-main>
  102. </el-container>
  103. <div v-if="enableBox" class="queBoxer">
  104. <div class="left">
  105. <div class="tit">
  106. <i class="el-icon-shopping-cart-2" style="margin-bottom: 10px"></i>
  107. <div style="width: 17px; margin-bottom: 10px">试题篮</div>
  108. <span class="count" style="background: #ff4e00; padding: 2px 5px; margin-bottom: 15px">{{
  109. queCardForm.queList.length
  110. }}</span>
  111. </div>
  112. <div style="cursor: pointer" @click="show = !show">
  113. <i :class="show ? 'el-icon-arrow-right' : 'el-icon-arrow-left'"></i>
  114. </div>
  115. </div>
  116. <div class="main">
  117. <el-collapse-transition>
  118. <div v-show="show">
  119. <div style="margin-bottom: 30px">
  120. <div style="margin-bottom: 10px">
  121. 共({{ queCardForm.queList.length }})道题
  122. </div>
  123. <div v-for="(value, key) in groupedCardQueList" :key="key + value.length"
  124. style="margin-top: 3px; color: #666666">
  125. <span>{{ key }}</span><span> {{ value.length }} </span>道
  126. </div>
  127. </div>
  128. <div>
  129. <p style="
  130. color: #ff4e00;
  131. text-align: right;
  132. border-bottom: 1px solid #dedede;
  133. ">
  134. <span @click="clearQueCard" style="cursor: pointer"> 清空</span>
  135. </p>
  136. </div>
  137. <div class="btn" @click="createdPaper">生成试卷</div>
  138. </div>
  139. </el-collapse-transition>
  140. </div>
  141. </div>
  142. <correct-question-dialog ref="correct"></correct-question-dialog>
  143. </div>
  144. </template>
  145. <script>
  146. import consts from '@/common/mx-const'
  147. import MxCondition from '@/components/MxCondition/mx-condition'
  148. import {
  149. addToQuestionCard,
  150. chapterTree,
  151. deleteQuestionCard,
  152. getQuestionCardList,
  153. knowledgeTree,
  154. listByChapter,
  155. listByKnowledge,
  156. queCancelCollect,
  157. queCollect
  158. } from '@/api/webApi/webQue'
  159. import CorrectQuestionDialog from '@/components/MxPaper/plus/correct-question-dialog'
  160. import LeftTreeSide from '@/views/questioncenter/components/generate-tabs/plugins/left-tree-side'
  161. import PaperWorkIdentifierMixin from './paper-work-identifier-mixin'
  162. import EventBus from '@/components/EventBus'
  163. export default {
  164. mixins: [PaperWorkIdentifierMixin],
  165. name: 'paper-by-hand',
  166. components: { LeftTreeSide, CorrectQuestionDialog, MxCondition },
  167. data() {
  168. return {
  169. queryParams: null,
  170. queryOutput: null,
  171. requireFields: [],
  172. chapterTree: [],
  173. knowledgeTree: [],
  174. treeProps: {
  175. label: 'name',
  176. children: 'children'
  177. },
  178. treeCache: {},
  179. treeCacheKey: '',
  180. currentNode: null,
  181. title: '',
  182. searchTerm: '',
  183. queList: [],
  184. queCardForm: {
  185. queList: []
  186. },
  187. total: 0,
  188. pageNum: 1,
  189. pageSize: 10,
  190. mathId: 'question_by_hand',
  191. show: false,
  192. enableBox: true,
  193. ignoreQType: false, // 智能组卷会乎略题型
  194. uploading: false
  195. }
  196. },
  197. computed: {
  198. isKnowledgeBranch() {
  199. return this.queryParams?.exeBranch == consts.enum.questionBranches.knowledge.value
  200. },
  201. branchTree() {
  202. return this.isKnowledgeBranch ? this.knowledgeTree : this.chapterTree
  203. },
  204. groupedCardQueList() {
  205. let groupedCard = {}
  206. this.queCardForm.queList.forEach((que) => {
  207. if (!groupedCard[que.qtpye]) groupedCard[que.qtpye] = []
  208. let groupedQueByType = groupedCard[que.qtpye]
  209. groupedQueByType.push(que)
  210. })
  211. return groupedCard
  212. }
  213. },
  214. watch: {
  215. 'queryParams.exeBranch': async function(newVal, oldVal) {
  216. if (newVal && oldVal) {
  217. // ignored first mx-condition model assign
  218. await this.$nextTick()
  219. this.resetQueryParams(true)
  220. }
  221. }
  222. },
  223. mounted() {
  224. this.resetQueryParams()
  225. this.loadQuestionCard()
  226. EventBus.instance.$on(consts.keys.keyGenerationCartChanged, () => this.loadQuestionCard())
  227. },
  228. beforeDestroy() {
  229. EventBus.instance.$off(consts.keys.keyGenerationCartChanged)
  230. },
  231. methods: {
  232. loadQuestionCard() {
  233. getQuestionCardList(this.extraData).then((res) => {
  234. this.queCardForm.queList = res.data
  235. })
  236. },
  237. resetQueryParams(force = false) {
  238. if (!this.queryParams || force) {
  239. const model = {}
  240. model.exeBranch = this.queryParams?.exeBranch || consts.enum.questionBranches.chapter.value
  241. switch (model.exeBranch) {
  242. case consts.enum.questionBranches.chapter.value:
  243. model.exeSubject = '' // 科目
  244. model.exeOrder = '' // 版本
  245. model.exeGrade = '' // 学册
  246. if (!this.ignoreQType) model.exeQTypeChapter = '' // 题型
  247. this.requireFields = ['exeBranch', 'exeSubject', 'exeOrder', 'exeGrade']
  248. if (this.isPaperWork && !this.ignoreQType) this.requireFields.push('exeQTypeChapter')
  249. break
  250. case consts.enum.questionBranches.knowledge.value:
  251. model.exeSubject = '' // 科目
  252. if (!this.ignoreQType) model.exeQTypeKnowledge = '' // 题型
  253. this.requireFields = ['exeBranch', 'exeSubject']
  254. if (this.isPaperWork && !this.ignoreQType) this.requireFields.push('exeQTypeKnowledge')
  255. break
  256. default:
  257. console.log('unexpected type for branch', model.exeBranch)
  258. return
  259. }
  260. this.queryParams = model
  261. }
  262. },
  263. async handleQuery(model) {
  264. // temporary save & parameter check
  265. this.queryOutput = model
  266. delete this.queryOutput.exeBranch // 本页API调用并不需要这个参数
  267. const isKnowledgeToChapter = !this.isKnowledgeBranch && !this.queryOutput.gradeId
  268. const isChapterToKnowledge = this.isKnowledgeBranch && !!this.queryOutput.gradeId
  269. // console.log('中间状态?', isKnowledgeToChapter, isChapterToKnowledge)
  270. if (isKnowledgeToChapter || isChapterToKnowledge) return // 这是exeBranch条件切换时的中间状态,不要触发后续查询
  271. this.cleanBeforeQuery()
  272. // refresh tree by need
  273. // TODO: 这里最好是由mx-condition解析依赖关系,但现在mx-condition的渲染逻辑不太方便切入
  274. const treeCacheObj = { ...this.queryOutput }
  275. delete treeCacheObj.qtpye // 树和题型无关
  276. const newTreeCacheKey = `${model.exeBranch}_${JSON.stringify(treeCacheObj)}`
  277. if (newTreeCacheKey != this.treeCacheKey) {
  278. this.treeCacheKey = newTreeCacheKey
  279. const treeFunc = this.isKnowledgeBranch ? knowledgeTree : chapterTree
  280. let treeRes = this.treeCache[this.treeCacheKey]
  281. if (!treeRes) {
  282. treeRes = await treeFunc(this.queryOutput)
  283. this.treeCache[this.treeCacheKey] = treeRes
  284. }
  285. this.branchTree.length = 0 // clear
  286. this.branchTree.push(...treeRes.data)
  287. // current & query question
  288. if (!this.branchTree.length) return
  289. const firstNode = this.branchTree[0]
  290. this.setCurrentNodeAndTitle(firstNode)
  291. await this.$nextTick()
  292. this.$refs.branchTree.setCurrentNode(firstNode)
  293. }
  294. // query question
  295. console.log('call resetQuestionQuery by mx-condition')
  296. this.resetQuestionQuery()
  297. },
  298. cleanBeforeQuery() {
  299. this.queList = []
  300. this.currentNode = null
  301. this.title = ''
  302. },
  303. setCurrentNodeAndTitle(nodeData) {
  304. const branchKey = this.queryParams.exeBranch + 'Id'
  305. const branchParam = {}
  306. branchParam[branchKey] = nodeData.id
  307. this.currentNode = branchParam
  308. this.title = nodeData.name
  309. },
  310. handleTreeNodeClick(data) {
  311. this.setCurrentNodeAndTitle(data)
  312. console.log('call resetQuestionQuery by tree node click')
  313. this.resetQuestionQuery()
  314. },
  315. resetQuestionQuery() {
  316. this.pageNum = 1
  317. this.queryQuestions()
  318. },
  319. async queryQuestions() {
  320. if (!this.currentNode) return
  321. const commit = {
  322. pageNum: this.pageNum,
  323. pageSize: this.pageSize,
  324. ...this.queryOutput,
  325. ...this.currentNode,
  326. searchTerm: this.searchTerm
  327. }
  328. const queryFunc = this.isKnowledgeBranch ? listByKnowledge : listByChapter
  329. const res = await queryFunc(commit)
  330. this.total = res.total
  331. this.queList = res.rows.map(r => {
  332. r.expand = false // add reactive field
  333. return r
  334. })
  335. await this.$nextTick()
  336. this.mxGlobal.MathQueue(this.mathId)
  337. },
  338. /* methods of question box */
  339. addQueCard(item) {
  340. if (this.uploading) return
  341. if (this.queCardForm.queList.includes(item.id)) return
  342. if (this.queCardForm.queList.length == 0) {
  343. // 为0直接添加
  344. this.uploading = true
  345. addToQuestionCard({
  346. questionId: item.id,
  347. ...this.extraData
  348. }).then(() => {
  349. this.queCardForm.queList.push(item)
  350. }).finally(() => this.uploading = false)
  351. } else {
  352. // 判断是否是同一个科目下的题目
  353. if (item.subjectid == this.queCardForm.queList[0].subjectid) {
  354. // 是,则添加
  355. this.uploading = true
  356. addToQuestionCard({
  357. questionId: item.id,
  358. ...this.extraData
  359. }).then((res) => {
  360. this.queCardForm.queList.push(item)
  361. }).finally(() => this.uploading = false)
  362. } else {
  363. // 不是,弹出警告
  364. this.$message.error('科目不一致,请清空试题篮或选择同一科目')
  365. }
  366. }
  367. },
  368. clearQueCard() {
  369. if (this.queCardForm.queList.length > 0) {
  370. deleteQuestionCard(this.extraData).then((res) => {
  371. console.log(res)
  372. this.queCardForm.queList = []
  373. this.msgSuccess('清空成功')
  374. })
  375. }
  376. },
  377. toCollectQue(item) {
  378. queCollect({
  379. questionId: item.id
  380. }).then((res) => {
  381. item.collect = !item.collect
  382. this.msgSuccess('收藏成功')
  383. console.log(res)
  384. })
  385. },
  386. toCancelCollectQue(item) {
  387. queCancelCollect({
  388. questionId: item.id
  389. }).then((res) => {
  390. item.collect = !item.collect
  391. this.msgSuccess('取消收藏成功')
  392. console.log(res)
  393. })
  394. },
  395. viewDetail(item) {
  396. item.expand = !item.expand
  397. },
  398. createdPaper() {
  399. if (this.queCardForm?.queList?.length > 0) {
  400. localStorage.setItem(
  401. 'paperData',
  402. JSON.stringify({
  403. paperTitle: null,
  404. subjectId: this.queryOutput.subjectId
  405. })
  406. )
  407. localStorage.setItem(
  408. 'questionList',
  409. JSON.stringify(this.queCardForm.queList)
  410. )
  411. this.$router.push({
  412. path: '/question-center/generatingPaperCenter/paper',
  413. query: this.extraData
  414. })
  415. }
  416. }
  417. }
  418. }
  419. </script>
  420. <style scoped>
  421. </style>