EditPaper.vue 83 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655
  1. <template>
  2. <div
  3. v-loading="loading"
  4. class="edit-paper"
  5. element-loading-text="拼命加载中。。。"
  6. >
  7. <div class="edit-header">
  8. <div class="edit-header-top box-justify">
  9. <div class="header-info">
  10. <div class="header-info-item">
  11. <span>课程代码:</span>
  12. <span>{{ paper.course.code }}</span>
  13. </div>
  14. <div class="header-info-item">
  15. <span>课程名称:</span>
  16. <span>{{ paper.course.name }}</span>
  17. </div>
  18. <div class="header-info-item">
  19. <span>试卷名称:</span>
  20. <span>
  21. <el-tooltip class="item" effect="dark" placement="top-start">
  22. <div slot="content">{{ paper.name }}</div>
  23. <el-input
  24. v-model="paper.name"
  25. size="mini"
  26. class="header-info-input"
  27. placeholder="试卷名称"
  28. ></el-input>
  29. </el-tooltip>
  30. </span>
  31. </div>
  32. <div class="header-info-item">
  33. <span>试卷总分:</span>
  34. <span>{{ paper.totalScore }}</span>
  35. </div>
  36. </div>
  37. <div class="header-btns">
  38. <el-button type="primary" size="small" @click="savePaper">
  39. 保存
  40. </el-button>
  41. <el-button
  42. type="danger"
  43. plain
  44. size="small"
  45. @click="deletePaper(paper.id)"
  46. >
  47. 删除
  48. </el-button>
  49. <el-button
  50. v-if="showCheckDuplicateBtn"
  51. type="primary"
  52. size="small"
  53. plain
  54. @click="checkDuplicate"
  55. >
  56. 进入查重
  57. </el-button>
  58. <el-dropdown>
  59. <el-button type="primary" size="small" plain>
  60. 更多<i class="el-icon-more el-icon--right"></i>
  61. </el-button>
  62. <el-dropdown-menu slot="dropdown" class="action-dropdown">
  63. <el-dropdown-item>
  64. <el-button type="primary" plain size="small" @click="openDialog"
  65. >上传音频
  66. </el-button>
  67. </el-dropdown-item>
  68. <el-dropdown-item>
  69. <el-button
  70. size="small"
  71. type="primary"
  72. plain
  73. @click="exportPaperAnswer()"
  74. >导出答案</el-button
  75. >
  76. </el-dropdown-item>
  77. <el-dropdown-item>
  78. <el-button
  79. type="primary"
  80. size="small"
  81. plain
  82. @click="openAnswerDialog"
  83. >导入答案
  84. </el-button>
  85. </el-dropdown-item>
  86. </el-dropdown-menu>
  87. </el-dropdown>
  88. <el-button
  89. size="small"
  90. type="danger"
  91. plain
  92. icon="icon icon-back"
  93. @click="back"
  94. >返回</el-button
  95. >
  96. </div>
  97. </div>
  98. <div class="edit-header-bottom box-justify">
  99. <div>
  100. <el-button type="info" size="small" @click="showBasicDialog">
  101. 基础构成
  102. </el-button>
  103. <el-button type="info" size="small" @click="showTypeDialog">
  104. 题型分布
  105. </el-button>
  106. <el-button type="info" size="small" @click="showBlueDialog">
  107. 蓝图分布
  108. </el-button>
  109. <el-button type="info" size="small" @click="showAuditDialog">
  110. 审核记录
  111. </el-button>
  112. <el-button type="info" size="small" @click="toEditCard">
  113. 编辑题卡
  114. </el-button>
  115. </div>
  116. <div>
  117. <el-button type="primary" size="small" plain @click="quesTagShowHide">
  118. {{ quesTagShow ? "隐藏" : "显示" }}属性
  119. </el-button>
  120. <el-button
  121. type="primary"
  122. size="small"
  123. plain
  124. @click="quesAnswerShowHide"
  125. >
  126. {{ quesAnswerShow ? "隐藏" : "显示" }}答案
  127. </el-button>
  128. </div>
  129. </div>
  130. </div>
  131. <div class="edit-body">
  132. <div class="edit-part-list">
  133. <div class="edit-part">
  134. <div class="edit-cont">
  135. <div class="edit-cont-title">
  136. <h3>考试说明</h3>
  137. </div>
  138. <div class="edit-cont-action">
  139. <el-button
  140. type="primary"
  141. plain
  142. size="small"
  143. @click="openEditExamPaperRemark"
  144. >编辑</el-button
  145. >
  146. </div>
  147. <div class="edit-cont-body">
  148. <rich-text :text-json="paper.examRemark"></rich-text>
  149. </div>
  150. </div>
  151. </div>
  152. </div>
  153. <!-- 循环大题 -->
  154. <div
  155. v-for="(paperDetail, detailIndex) in paper.paperDetails"
  156. v-show="paperDetailShow(paperDetail)"
  157. :key="detailIndex"
  158. class="edit-part-list"
  159. >
  160. <div class="edit-part">
  161. <div
  162. class="edit-cont"
  163. @mouseover="quesMouseOver(paperDetail.id)"
  164. @mouseout="quesMouseOut(paperDetail.id)"
  165. >
  166. <div class="edit-cont-title">
  167. <h3>
  168. <span>{{ paperDetail.cnNum }}</span> <span>.</span>
  169. <span>{{ paperDetail.name }}</span>
  170. <span>
  171. ({{
  172. !paperDetail.title ? "本大题" : paperDetail.title + ","
  173. }}共{{ paperDetail.unitCount }}小题,满分{{
  174. paperDetail.score
  175. }}分)
  176. </span>
  177. </h3>
  178. </div>
  179. <div
  180. :id="paperDetail.id"
  181. class="edit-cont-action"
  182. style="visibility: hidden"
  183. >
  184. <el-button
  185. v-show="parentView == 'gen_paper'"
  186. size="small"
  187. type="primary"
  188. plain
  189. @click="selectQues(paperDetail.id)"
  190. >选题
  191. </el-button>
  192. <el-button
  193. size="small"
  194. type="primary"
  195. plain
  196. @click="openEditPaperDetail(paperDetail)"
  197. >编辑
  198. </el-button>
  199. <el-button
  200. v-if="showUp(paperDetail)"
  201. size="small"
  202. type="primary"
  203. plain
  204. @click="movePaperDetail(paperDetail, 'up')"
  205. >上移
  206. </el-button>
  207. <el-button
  208. v-if="showDown(paperDetail)"
  209. size="small"
  210. type="primary"
  211. plain
  212. @click="movePaperDetail(paperDetail, 'down')"
  213. >下移
  214. </el-button>
  215. <el-button
  216. size="small"
  217. type="danger"
  218. @click="deletePaperDetail(paperDetail.id)"
  219. >删除
  220. </el-button>
  221. <el-button
  222. v-show="showButtons[detailIndex].up"
  223. size="small"
  224. type="primary"
  225. plain
  226. icon="el-icon-arrow-up"
  227. @click.stop="hideContent(detailIndex)"
  228. ></el-button>
  229. <el-button
  230. v-show="!showButtons[detailIndex].up"
  231. size="small"
  232. icon="el-icon-arrow-down"
  233. type="primary"
  234. plain
  235. @click.stop="showContent(detailIndex)"
  236. ></el-button>
  237. </div>
  238. <rich-text
  239. class="edit-cont-body"
  240. :text-json="paperDetail.description"
  241. ></rich-text>
  242. </div>
  243. <div v-show="quesTagShow" class="edit-property">
  244. <div class="edit-property-box">
  245. <div
  246. v-for="(paperDetailTag, tagIndex) in paperDetail.tags"
  247. :key="tagIndex"
  248. class="edit-property-item"
  249. >
  250. <div class="edit-property-body edit-property-danger">
  251. <div class="edit-property-title">
  252. {{ paperDetailTag.tag }}
  253. </div>
  254. <div class="edit-property-content">
  255. {{ paperDetailTag.content }}
  256. </div>
  257. </div>
  258. </div>
  259. </div>
  260. </div>
  261. </div>
  262. <!-- 循环小题 -->
  263. <div
  264. v-show="showQuestions[detailIndex].is_show"
  265. class="edit-paper-questions"
  266. >
  267. <template
  268. v-for="(paperDetailUnit, unitIndex) in paperDetail.paperDetailUnits"
  269. >
  270. <div
  271. v-show="quesShow(paperDetailUnit.id)"
  272. :key="`question-${unitIndex}`"
  273. :class="[
  274. 'edit-part',
  275. {
  276. 'question-duplicate':
  277. paperDetailUnit.question.checkDuplicateStatus ==
  278. 'TO_BE_DISPOSE',
  279. },
  280. ]"
  281. >
  282. <div
  283. class="edit-cont"
  284. @mouseover="quesMouseOver(paperDetailUnit.id)"
  285. @mouseout="quesMouseOut(paperDetailUnit.id)"
  286. >
  287. <div class="edit-cont-title">
  288. <span>{{ paperDetailUnit.number }}.</span>
  289. <rich-text
  290. :text-json="paperDetailUnit.question.quesBody"
  291. ></rich-text>
  292. <span> ({{ paperDetailUnit.score }}分) </span>
  293. </div>
  294. <div
  295. :id="paperDetailUnit.id"
  296. class="edit-cont-action"
  297. style="visibility: hidden"
  298. >
  299. <span class="tips-info">
  300. {{ paperDetailUnit.question.bodyLengthText }}
  301. </span>
  302. <el-button
  303. v-if="
  304. paperDetailUnit.question.checkDuplicateStatus ==
  305. 'TO_BE_DISPOSE'
  306. "
  307. size="small"
  308. type="primary"
  309. plain
  310. @click="checkDuplicateQuestion(paperDetailUnit.question.id)"
  311. >进入查重
  312. </el-button>
  313. <el-button
  314. size="small"
  315. type="primary"
  316. plain
  317. @click="editQues(paperDetailUnit, paperDetailUnit.question)"
  318. >编辑
  319. </el-button>
  320. <el-button
  321. v-if="showUnitUp(paperDetail, paperDetailUnit.id)"
  322. size="small"
  323. type="primary"
  324. plain
  325. @click="
  326. movePaperDetailUnit(
  327. paperDetail.id,
  328. paperDetailUnit.id,
  329. 'up'
  330. )
  331. "
  332. >上移
  333. </el-button>
  334. <el-button
  335. v-if="showUnitDown(paperDetail, paperDetailUnit.id)"
  336. size="small"
  337. type="primary"
  338. plain
  339. @click="
  340. movePaperDetailUnit(
  341. paperDetail.id,
  342. paperDetailUnit.id,
  343. 'down'
  344. )
  345. "
  346. >下移
  347. </el-button>
  348. <el-button
  349. type="danger"
  350. size="small"
  351. @click="deleteQues(paperDetailUnit)"
  352. >删除
  353. </el-button>
  354. <el-button
  355. v-show="
  356. isNested(paperDetailUnit.questionType) &&
  357. showSubButtons[detailIndex + '-' + unitIndex]
  358. "
  359. size="small"
  360. icon="el-icon-arrow-up"
  361. @click.stop="hideSubContent(detailIndex + '-' + unitIndex)"
  362. ></el-button>
  363. <el-button
  364. v-show="
  365. isNested(paperDetailUnit.questionType) &&
  366. !showSubButtons[detailIndex + '-' + unitIndex]
  367. "
  368. size="small"
  369. type="primary"
  370. plain
  371. icon="el-icon-arrow-down"
  372. @click.stop="showSubContent(detailIndex + '-' + unitIndex)"
  373. ></el-button>
  374. </div>
  375. <div class="edit-cont-body">
  376. <div
  377. v-for="(quesOption, optionIndex) in paperDetailUnit.question
  378. .quesOptions"
  379. :key="optionIndex"
  380. class="paper-option"
  381. >
  382. <span>{{ optionIndex | optionOrderWordFilter }}. </span>
  383. <rich-text :text-json="quesOption.optionBody"></rich-text>
  384. </div>
  385. <div v-if="!isNested(paperDetailUnit.questionType)">
  386. <div v-show="quesAnswerShow" class="paper-answer">
  387. <span>答案:</span>
  388. <question-answer
  389. :data="paperDetailUnit.question"
  390. ></question-answer>
  391. </div>
  392. </div>
  393. </div>
  394. </div>
  395. <div v-show="quesTagShow" class="edit-property">
  396. <div class="edit-property-box">
  397. <div
  398. v-for="(questionTag, tagIndex) in paperDetailUnit.question
  399. .tags"
  400. :key="tagIndex"
  401. class="edit-property-item"
  402. >
  403. <div class="edit-property-body">
  404. <div class="edit-property-title">
  405. {{ questionTag.tag }}
  406. </div>
  407. <div class="edit-property-content">
  408. {{ questionTag.content }}
  409. </div>
  410. </div>
  411. </div>
  412. </div>
  413. </div>
  414. </div>
  415. <div
  416. v-show="showSubQuestions[detailIndex + '-' + unitIndex]"
  417. :key="`question-sub-${unitIndex}`"
  418. class="edit-paper-question-subs"
  419. >
  420. <div
  421. v-for="(subQuestion, subIndex) in paperDetailUnit.question
  422. .subQuestions"
  423. v-show="quesShow(subQuestion.id)"
  424. :key="subIndex"
  425. class="edit-part"
  426. >
  427. <div
  428. class="edit-cont"
  429. @mouseover="
  430. quesMouseOver(
  431. getSubQuesEditId(paperDetailUnit, subQuestion)
  432. )
  433. "
  434. @mouseout="
  435. quesMouseOut(getSubQuesEditId(paperDetailUnit, subQuestion))
  436. "
  437. >
  438. <div
  439. :id="getSubQuesEditId(paperDetailUnit, subQuestion)"
  440. class="edit-cont-action"
  441. style="visibility: hidden"
  442. >
  443. <el-button
  444. size="small"
  445. type="primary"
  446. plain
  447. @click="editQues(paperDetailUnit, subQuestion)"
  448. >编辑
  449. </el-button>
  450. <el-button
  451. v-if="showUnitSubUp(paperDetailUnit, subQuestion.id)"
  452. size="small"
  453. type="primary"
  454. plain
  455. @click="
  456. movePaperDetailUnitSub(
  457. paperDetailUnit.id,
  458. subQuestion.id,
  459. 'up'
  460. )
  461. "
  462. >上移
  463. </el-button>
  464. <el-button
  465. v-if="showUnitSubDown(paperDetailUnit, subQuestion.id)"
  466. size="small"
  467. type="primary"
  468. plain
  469. @click="
  470. movePaperDetailUnitSub(
  471. paperDetailUnit.id,
  472. subQuestion.id,
  473. 'down'
  474. )
  475. "
  476. >下移
  477. </el-button>
  478. </div>
  479. <div class="edit-cont-title">
  480. <span>{{ subQuestion.subNumber }}. </span>
  481. <rich-text :text-json="subQuestion.quesBody"></rich-text>
  482. <span
  483. >({{ paperDetailUnit.subScoreList[subIndex] }}分)</span
  484. >
  485. </div>
  486. <div
  487. v-if="!isMatchingQuestion(paperDetailUnit.questionType)"
  488. class="edit-cont-body"
  489. >
  490. <div
  491. v-for="(
  492. subQuesOption, subOptIndex
  493. ) in subQuestion.quesOptions"
  494. :key="subOptIndex"
  495. class="paper-option"
  496. >
  497. <span>{{ subOptIndex | optionOrderWordFilter }}. </span>
  498. <rich-text
  499. :text-json="subQuesOption.optionBody"
  500. ></rich-text>
  501. </div>
  502. </div>
  503. <div v-show="quesAnswerShow" class="paper-answer">
  504. <span>答案:</span>
  505. <question-answer :data="subQuestion"></question-answer>
  506. <!-- <rich-text :text-json="subQuestion.quesAnswer"></rich-text> -->
  507. </div>
  508. </div>
  509. <div v-show="quesTagShow" class="edit-property">
  510. <div class="edit-property-box">
  511. <div
  512. v-for="(subQuestionTag, tagIndex) in subQuestion.tags"
  513. :key="tagIndex"
  514. class="edit-property-item"
  515. >
  516. <div class="edit-property-body">
  517. <div class="edit-property-title">
  518. {{ subQuestionTag.tag }}
  519. </div>
  520. <div class="edit-property-content">
  521. {{ subQuestionTag.content }}
  522. </div>
  523. </div>
  524. </div>
  525. </div>
  526. </div>
  527. </div>
  528. </div>
  529. </template>
  530. </div>
  531. </div>
  532. </div>
  533. <!-- 编辑大题弹框 -->
  534. <el-dialog
  535. v-loading.body="detailLoading"
  536. width="600px"
  537. title="大题名称编辑"
  538. element-loading-text="保存中。。。"
  539. :visible.sync="paperDatailDialog"
  540. :modal="false"
  541. append-to-body
  542. custom-class="side-dialog"
  543. @close="closeQuesDialog"
  544. >
  545. <el-form :model="editpaperDetail" label-width="80px">
  546. <el-form-item label="大题名称">
  547. <el-input
  548. v-model="editpaperDetail.name"
  549. placeholder="请输入大题名称"
  550. ></el-input>
  551. </el-form-item>
  552. <el-form-item label="大题描述">
  553. <v-editor v-model="editpaperDetail.description"></v-editor>
  554. </el-form-item>
  555. </el-form>
  556. <div slot="footer">
  557. <el-button type="primary" @click="savePaperDatail(editpaperDetail)"
  558. >保存</el-button
  559. >
  560. <el-button type="danger" plain @click="closePaperDatailDialog()"
  561. >取消</el-button
  562. >
  563. </div>
  564. </el-dialog>
  565. <!-- 编辑试题弹框 -->
  566. <el-dialog
  567. v-loading.body="dialogLoading"
  568. title="试题编辑"
  569. width="900px"
  570. element-loading-text="保存中。。。"
  571. :visible.sync="quesDialog"
  572. :modal="false"
  573. append-to-body
  574. custom-class="side-dialog"
  575. @close="closeQuesDialog"
  576. >
  577. <el-form :model="quesModel" label-position="right" label-width="80px">
  578. <el-row :gutter="10">
  579. <el-col :span="12">
  580. <el-form-item label="题型">
  581. <el-select
  582. v-model="quesModel.questionType"
  583. :disabled="true"
  584. placeholder="请输入题型"
  585. >
  586. <el-option
  587. v-for="item in questionTypes"
  588. :key="item.value"
  589. :label="item.label"
  590. :value="item.value"
  591. >
  592. </el-option>
  593. </el-select>
  594. </el-form-item>
  595. </el-col>
  596. <el-col :span="12">
  597. <el-form-item label="分值">
  598. <el-input-number
  599. v-model="quesModel.score"
  600. placeholder="分值"
  601. :precision="1"
  602. :min="0"
  603. :disabled="isNested(quesModel.questionType)"
  604. ></el-input-number>
  605. </el-form-item>
  606. </el-col>
  607. <el-col :span="12">
  608. <el-form-item label="难度">
  609. <el-select
  610. v-model="quesModel.difficultyDegree"
  611. placeholder="请输入难度"
  612. :disabled="
  613. isNested(quesModel.questionType) ? true : updatePorperty
  614. "
  615. >
  616. <el-option
  617. v-for="item in difficultyDegreeList"
  618. :key="item.value"
  619. :label="item.label"
  620. :value="item.value"
  621. >
  622. </el-option>
  623. </el-select>
  624. </el-form-item>
  625. </el-col>
  626. <el-col :span="12">
  627. <el-form-item label="公开度">
  628. <el-select
  629. v-model="quesModel.publicity"
  630. placeholder="请输入公开度"
  631. :disabled="updatePorperty"
  632. >
  633. <el-option
  634. v-for="item in publicityList"
  635. :key="item.value"
  636. :label="item.label"
  637. :value="item.value"
  638. >
  639. </el-option>
  640. </el-select>
  641. </el-form-item>
  642. </el-col>
  643. <el-col
  644. v-if="quesModel.questionType == 'TEXT_ANSWER_QUESTION'"
  645. :span="12"
  646. >
  647. <el-form-item label="作答类型">
  648. <el-select
  649. v-model="quesModel.answerType"
  650. :disabled="updatePorperty"
  651. >
  652. <el-option
  653. v-for="item in answerTypes"
  654. :key="item.value"
  655. :label="item.label"
  656. :value="item.value"
  657. >
  658. </el-option>
  659. </el-select>
  660. </el-form-item>
  661. </el-col>
  662. <el-col
  663. v-if="quesModel.questionType && !isNested(quesModel.questionType)"
  664. :span="12"
  665. >
  666. <el-form-item label="时长">
  667. <el-input-number
  668. v-model="quesModel.control.maxAnswerTime"
  669. size="small"
  670. :precision="0"
  671. :min="1"
  672. :disabled="updatePorperty"
  673. ></el-input-number>
  674. </el-form-item>
  675. </el-col>
  676. <el-col v-if="isMatchingQuestion(quesModel.questionType)" :span="12">
  677. <el-form-item label="答题模式">
  678. <el-select
  679. v-model="quesModel.quesParam.matchingMode"
  680. :disabled="updatePorperty"
  681. >
  682. <el-option
  683. v-for="item in matchingModes"
  684. :key="item.value"
  685. :label="item.label"
  686. :value="item.value"
  687. >
  688. </el-option>
  689. </el-select>
  690. </el-form-item>
  691. </el-col>
  692. </el-row>
  693. <el-form-item label="属性列表">
  694. <el-tooltip
  695. v-for="(content, propIndex) in quesModel.quesProperties"
  696. :key="propIndex"
  697. placement="top"
  698. >
  699. <div slot="content">
  700. <span v-if="content.firstProperty != null"
  701. >一级属性:{{ content.firstProperty.name }}({{
  702. content.firstProperty.code
  703. }})</span
  704. ><br />
  705. <span v-if="content.secondProperty != null"
  706. >二级属性:{{ content.secondProperty.name }}({{
  707. content.secondProperty.code
  708. }})</span
  709. >
  710. </div>
  711. <span>
  712. <el-tag
  713. :key="content.id"
  714. style="margin-right: 5px"
  715. :closable="!updatePorperty"
  716. type="primary"
  717. effect="dark"
  718. @close="handleClose(content)"
  719. >
  720. {{ content.courseProperty.name }}
  721. </el-tag>
  722. </span>
  723. </el-tooltip>
  724. </el-form-item>
  725. <el-row :gutter="10">
  726. <el-col :span="8">
  727. <el-form-item label="属性名" label-width="80px">
  728. <el-select
  729. v-model="coursePropertyId"
  730. placeholder="属性名"
  731. class="property_with"
  732. :disabled="updatePorperty"
  733. @change="searchFirst"
  734. >
  735. <el-option label="请选择" value=""></el-option>
  736. <el-option
  737. v-for="item in coursePropertyList"
  738. :key="item.id"
  739. :label="item.name"
  740. :value="item.id"
  741. >
  742. </el-option>
  743. </el-select>
  744. </el-form-item>
  745. </el-col>
  746. <el-col :span="6">
  747. <el-form-item label="一级" label-width="48px">
  748. <el-select
  749. v-model="firstPropertyId"
  750. placeholder="一级"
  751. class="property_with"
  752. :disabled="updatePorperty"
  753. @change="searchSecond"
  754. >
  755. <el-option label="请选择" value=""></el-option>
  756. <el-option
  757. v-for="item in firstPropertyList"
  758. :key="item.id"
  759. :label="item.name + '(' + item.code + ')'"
  760. :value="item.id"
  761. >
  762. </el-option>
  763. </el-select>
  764. </el-form-item>
  765. </el-col>
  766. <el-col :span="6">
  767. <el-form-item label="二级" label-width="48px">
  768. <el-select
  769. v-model="secondPropertyId"
  770. placeholder="二级"
  771. class="property_with"
  772. :disabled="updatePorperty"
  773. >
  774. <el-option label="请选择" value=""></el-option>
  775. <el-option
  776. v-for="item in secondPropertyList"
  777. :key="item.id"
  778. :label="item.name + '(' + item.code + ')'"
  779. :value="item.id"
  780. >
  781. </el-option>
  782. </el-select>
  783. </el-form-item>
  784. </el-col>
  785. <el-col :span="4">
  786. <el-form-item label-width="0px">
  787. <el-button
  788. type="primary"
  789. :disabled="updatePorperty"
  790. @click="insertProperty"
  791. ><i class="el-icon-plus"></i>新增属性
  792. </el-button>
  793. </el-form-item>
  794. </el-col>
  795. </el-row>
  796. <!-- end by weiwenhai -->
  797. <el-form-item label="题目">
  798. <v-editor
  799. v-model="quesModel.quesBody"
  800. :enable-answer-point="enableAnswerPoint"
  801. @change="quesBodyChange"
  802. ></v-editor>
  803. </el-form-item>
  804. <el-form-item
  805. v-for="(quesOption, optIndex) in quesModel.quesOptions"
  806. :key="optIndex"
  807. >
  808. <div class="question-edit-option">
  809. <div class="option-check">
  810. <div
  811. v-if="
  812. !quesModel.parentType ||
  813. !isMatchingQuestion(quesModel.parentType) ||
  814. !isInOtherSelect(optIndex, quesModel)
  815. "
  816. >
  817. <el-radio
  818. v-if="quesModel.questionType === 'SINGLE_ANSWER_QUESTION'"
  819. v-model="singleRightAnswer"
  820. :label="optIndex | optionOrderWordFilter"
  821. ></el-radio>
  822. <el-checkbox
  823. v-if="quesModel.questionType === 'MULTIPLE_ANSWER_QUESTION'"
  824. v-model="multipleRightAnswer"
  825. :label="optIndex | optionOrderWordFilter"
  826. ></el-checkbox>
  827. <span v-if="isMatchingQuestion(quesModel.questionType)">{{
  828. optIndex | optionOrderWordFilter
  829. }}</span>
  830. </div>
  831. <div
  832. v-if="
  833. quesModel.parentType &&
  834. isMatchingQuestion(quesModel.parentType) &&
  835. isInOtherSelect(optIndex, quesModel)
  836. "
  837. >
  838. <el-tooltip
  839. class="item"
  840. effect="dark"
  841. :content="otherSelect(optIndex, quesModel)"
  842. placement="top-start"
  843. >
  844. <el-radio
  845. v-if="quesModel.questionType === 'SINGLE_ANSWER_QUESTION'"
  846. v-model="singleRightAnswer"
  847. :disabled="true"
  848. :label="optIndex | optionOrderWordFilter"
  849. ></el-radio>
  850. <el-checkbox
  851. v-if="quesModel.questionType === 'MULTIPLE_ANSWER_QUESTION'"
  852. v-model="multipleRightAnswer"
  853. :disabled="true"
  854. :label="optIndex | optionOrderWordFilter"
  855. ></el-checkbox>
  856. <span v-if="isMatchingQuestion(quesModel.questionType)">{{
  857. optIndex | optionOrderWordFilter
  858. }}</span>
  859. </el-tooltip>
  860. </div>
  861. </div>
  862. <div
  863. v-if="
  864. !quesModel.parentType ||
  865. !isMatchingQuestion(quesModel.parentType)
  866. "
  867. class="option-body"
  868. >
  869. <v-editor v-model="quesOption.optionBody"></v-editor>
  870. </div>
  871. <div
  872. v-if="
  873. quesModel.parentType && isMatchingQuestion(quesModel.parentType)
  874. "
  875. class="option-body"
  876. >
  877. <rich-text :text-json="quesOption.optionBody"></rich-text>
  878. </div>
  879. <div
  880. v-if="
  881. !quesModel.parentType ||
  882. !isMatchingQuestion(quesModel.parentType)
  883. "
  884. class="option-delete"
  885. >
  886. <el-button
  887. size="mini"
  888. circle
  889. type="danger"
  890. icon="el-icon-delete"
  891. title="删除"
  892. @click.prevent="removeQuesOption(quesOption)"
  893. ></el-button>
  894. </div>
  895. </div>
  896. </el-form-item>
  897. <el-form-item>
  898. <el-button
  899. v-if="
  900. (quesModel.questionType == 'SINGLE_ANSWER_QUESTION' ||
  901. quesModel.questionType == 'MULTIPLE_ANSWER_QUESTION' ||
  902. isMatchingQuestion(quesModel.questionType)) &&
  903. !isMatchingQuestion(quesModel.parentType)
  904. "
  905. type="primary"
  906. @click="addQuesOption"
  907. ><i class="el-icon-plus"></i> 新增选项
  908. </el-button>
  909. </el-form-item>
  910. <!-- 答案 -->
  911. <!-- 填空, -->
  912. <div v-if="quesModel.questionType == 'FILL_BLANK_QUESTION'">
  913. <el-form-item label="答案" prop="quesAnswer">
  914. <div v-if="quesAnswer" class="option-list">
  915. <div
  916. v-for="(opt, index) of quesAnswer"
  917. :key="index"
  918. class="option-item"
  919. >
  920. <div class="option-item-info">
  921. <span>({{ index + 1 }})</span>
  922. </div>
  923. <v-editor
  924. :value="opt"
  925. class="option-item-body"
  926. @change="(val) => updateAnswerPoint(index, val)"
  927. />
  928. </div>
  929. </div>
  930. </el-form-item>
  931. </div>
  932. <!-- 简答 -->
  933. <div v-if="quesModel.questionType === 'TEXT_ANSWER_QUESTION'">
  934. <el-form-item label="答案" prop="quesAnswer">
  935. <v-editor
  936. v-model="quesAnswer"
  937. @change="textAnswerChange"
  938. ></v-editor>
  939. </el-form-item>
  940. </div>
  941. <!-- 单选或多选 -->
  942. <div
  943. v-if="
  944. quesModel.questionType == 'SINGLE_ANSWER_QUESTION' ||
  945. quesModel.questionType == 'MULTIPLE_ANSWER_QUESTION'
  946. "
  947. >
  948. <el-form-item label="答案">
  949. <span v-html="answer"></span>
  950. </el-form-item>
  951. </div>
  952. <!-- 判断 -->
  953. <div v-if="quesModel.questionType == 'BOOL_ANSWER_QUESTION'">
  954. <el-row>
  955. <el-col>
  956. <el-form-item label="答案">
  957. <el-select
  958. v-model="quesAnswer"
  959. placeholder="请选择"
  960. @change="boolAnswerChange"
  961. >
  962. <el-option
  963. v-for="op in options"
  964. :key="op.value"
  965. :label="op.label"
  966. :value="op.value"
  967. >
  968. </el-option>
  969. </el-select>
  970. </el-form-item>
  971. </el-col>
  972. </el-row>
  973. </div>
  974. </el-form>
  975. <div slot="footer">
  976. <el-button type="primary" @click="savePaperDetailUnit()"
  977. >保存</el-button
  978. >
  979. <el-button type="danger" plain @click="closeQuesDialog">取消</el-button>
  980. </div>
  981. </el-dialog>
  982. <!-- 考试说明弹框 -->
  983. <el-dialog
  984. title="考试说明编辑"
  985. :visible.sync="paperRemarkDialog"
  986. width="600px"
  987. :modal="false"
  988. append-to-body
  989. custom-class="side-dialog"
  990. >
  991. <el-form label-position="top">
  992. <el-form-item label="考试说明:">
  993. <v-editor v-model="examRemark"></v-editor>
  994. </el-form-item>
  995. </el-form>
  996. <div slot="footer">
  997. <el-button type="primary" @click="savePaperRemark">保存</el-button>
  998. <el-button type="danger" plain @click="closPaperRemark">取消</el-button>
  999. </div>
  1000. </el-dialog>
  1001. <!-- 上传音频弹框 -->
  1002. <el-dialog
  1003. title="上传音频文件"
  1004. :visible.sync="dialogRadioFile"
  1005. :before-close="closeAudioDialog"
  1006. :modal="false"
  1007. width="520px"
  1008. append-to-body
  1009. custom-class="side-dialog"
  1010. >
  1011. <div>
  1012. <div tabindex="0" class="el-upload el-upload--text">
  1013. <el-button
  1014. type="primary"
  1015. icon="icon icon-search-white"
  1016. @click="selectAudioFile"
  1017. >选择文件</el-button
  1018. >
  1019. <input
  1020. id="radioFile"
  1021. class="el-upload__input"
  1022. name="files"
  1023. type="file"
  1024. value="上传音频文件"
  1025. webkitdirectory
  1026. @change="audioFileChange"
  1027. />
  1028. </div>
  1029. <el-button type="warning" @click="checkFile">检查文件名</el-button>
  1030. <el-button
  1031. type="info"
  1032. :loading="uploadAudioLoading"
  1033. :disabled="isUpload || uploadAudioLoading"
  1034. @click="uploadAudioFile"
  1035. >
  1036. <span v-show="!uploadAudioLoading">开始上传</span>
  1037. <span v-show="uploadAudioLoading">正在上传中...</span>
  1038. </el-button>
  1039. </div>
  1040. <p v-if="audioFileName" class="tips-info">文件: {{ audioFileName }}</p>
  1041. <div v-if="checkResult" style="margin-top: 20px">
  1042. <span>检查结果:</span><br /><br />
  1043. <span v-show="message == 'OK!'" style="color: #13ce66">OK!</span>
  1044. <span v-show="message != 'OK!'" style="color: #ff4949">{{
  1045. message
  1046. }}</span>
  1047. </div>
  1048. </el-dialog>
  1049. <!-- 上传答案文件 -->
  1050. <el-dialog
  1051. title="上传答案文件"
  1052. :visible.sync="dialogAnswerFile"
  1053. :before-close="closeAnswerDialog"
  1054. :modal="false"
  1055. width="520px"
  1056. append-to-body
  1057. custom-class="side-dialog"
  1058. >
  1059. <div>
  1060. <div tabindex="0" class="el-upload el-upload--text">
  1061. <el-button
  1062. type="primary"
  1063. icon="icon icon-search-white"
  1064. @click="selectAnswerFile"
  1065. >选择文件</el-button
  1066. >
  1067. <input
  1068. id="answerFile"
  1069. class="el-upload__input"
  1070. name="answerFiles"
  1071. type="file"
  1072. value="上传答案文件"
  1073. accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
  1074. @change="answerFileChange"
  1075. />
  1076. </div>
  1077. <el-button type="primary" @click="downAnswerTemplate">
  1078. <span>下载模板</span>
  1079. </el-button>
  1080. <el-button
  1081. type="primary"
  1082. :loading="uploadAnswerLoading"
  1083. :disabled="uploadAnswerLoading"
  1084. @click="uploadAnswerFile"
  1085. >
  1086. <span v-show="!uploadAnswerLoading">开始上传</span>
  1087. <span v-show="uploadAnswerLoading">正在上传中...</span>
  1088. </el-button>
  1089. </div>
  1090. <p v-if="answerFileName" class="tips-info">文件: {{ answerFileName }}</p>
  1091. <div style="margin-top: 20px">
  1092. <span v-show="answerMessage != ''" style="color: #ff4949">{{
  1093. answerMessage
  1094. }}</span>
  1095. </div>
  1096. </el-dialog>
  1097. <!-- 基础构成 -->
  1098. <el-dialog
  1099. title="基础构成"
  1100. width="100%"
  1101. :visible.sync="basicDialog"
  1102. :modal="false"
  1103. append-to-body
  1104. custom-class="side-dialog"
  1105. >
  1106. <PaperBasicComposition
  1107. v-if="basicDialog"
  1108. :paper-id="paperId"
  1109. ></PaperBasicComposition>
  1110. </el-dialog>
  1111. <!-- 题型分布 -->
  1112. <el-dialog
  1113. title="题型分布"
  1114. width="100%"
  1115. :visible.sync="typeDialog"
  1116. :modal="false"
  1117. append-to-body
  1118. custom-class="side-dialog"
  1119. >
  1120. <PaperQuestionType
  1121. v-if="typeDialog"
  1122. :paper-id="paperId"
  1123. ></PaperQuestionType>
  1124. </el-dialog>
  1125. <!-- 蓝图分布 -->
  1126. <el-dialog
  1127. title="蓝图分布"
  1128. width="100%"
  1129. :visible.sync="blueDialog"
  1130. :modal="false"
  1131. append-to-body
  1132. custom-class="side-dialog"
  1133. >
  1134. <PaperBlue
  1135. v-if="blueDialog"
  1136. :paper-id="paperId"
  1137. :course-id="paper.course.id"
  1138. ></PaperBlue>
  1139. </el-dialog>
  1140. <!-- 审核记录 -->
  1141. <el-dialog
  1142. title="审核记录"
  1143. width="700px"
  1144. :visible.sync="auditInfoDialog"
  1145. :modal="false"
  1146. append-to-body
  1147. custom-class="side-dialog"
  1148. >
  1149. <AuditInfo v-if="auditInfoDialog" :paper-id="paperId"></AuditInfo>
  1150. </el-dialog>
  1151. </div>
  1152. </template>
  1153. <script>
  1154. import { QUESTION_API } from "@/constants/constants";
  1155. import { isEmptyStr, QUESTION_TYPES } from "../constants/constants";
  1156. import { mapState } from "vuex";
  1157. import PaperBasicComposition from "./PaperBasicComposition.vue";
  1158. import PaperQuestionType from "./PaperQuestionType.vue";
  1159. import PaperBlue from "./PaperBlue.vue";
  1160. import AuditInfo from "./AuditInfo.vue";
  1161. export default {
  1162. name: "EditPaperApp",
  1163. components: {
  1164. PaperBasicComposition,
  1165. PaperQuestionType,
  1166. PaperBlue,
  1167. AuditInfo,
  1168. },
  1169. data() {
  1170. return {
  1171. showCheckDuplicateBtn: false,
  1172. auditInfoDialog: false,
  1173. blueDialog: false,
  1174. typeDialog: false,
  1175. basicDialog: false,
  1176. quesAnswerShow: true,
  1177. quesTagShow: true,
  1178. hValue: "100px",
  1179. wValue: "560px",
  1180. display: "block",
  1181. uploadAction: "",
  1182. fileList: [],
  1183. answerFileList: [],
  1184. paperId: "",
  1185. paperDetailId: "",
  1186. editPaperDetailUnit: "",
  1187. quesDialog: false,
  1188. paperDatailDialog: false,
  1189. paperRemarkDialog: false,
  1190. parentView: "",
  1191. paper: {
  1192. course: {
  1193. code: "",
  1194. name: "",
  1195. },
  1196. examRemark: "",
  1197. },
  1198. loading: false,
  1199. dialogLoading: false,
  1200. detailLoading: false,
  1201. uploadAudioLoading: false,
  1202. uploadAnswerLoading: false,
  1203. questionTypes: QUESTION_TYPES,
  1204. questionType: "",
  1205. quesModel: { quesProperties: [] },
  1206. quesAnswer: null,
  1207. prevAnswerPointCount: 0,
  1208. editpaperDetail: {},
  1209. reduplicateQuestions: [],
  1210. reduplicateGroup: [],
  1211. reduplicateQuesColor: [],
  1212. singleRightAnswer: "", //接收单选答案
  1213. multipleRightAnswer: [], //接收多选答案
  1214. options: [
  1215. {
  1216. value: 0,
  1217. label: "错误",
  1218. },
  1219. {
  1220. value: 1,
  1221. label: "正确",
  1222. },
  1223. ],
  1224. duplicateLoading: false,
  1225. dialogRadioFile: false,
  1226. dialogAnswerFile: false,
  1227. isUpload: true,
  1228. isUploadAnswer: true,
  1229. message: "",
  1230. answerMessage: "",
  1231. answerFileName: "",
  1232. audioFileName: "",
  1233. checkResult: false,
  1234. checkResultAnswer: false,
  1235. fileNameList: [],
  1236. defaultColor: [
  1237. "Red",
  1238. "Blue",
  1239. "LimeGreen",
  1240. "GoldenRod",
  1241. "Black",
  1242. "BlueViolet",
  1243. "Chocolate",
  1244. "DarkCyan",
  1245. "HotPink",
  1246. "Orange",
  1247. "IndianRed",
  1248. "Indigo",
  1249. "Green",
  1250. "Aqua",
  1251. "CadetBlue",
  1252. "SkyBlue",
  1253. "SlateBlue",
  1254. "SlateGray",
  1255. "Tomato",
  1256. "VioletRed",
  1257. ],
  1258. difficultyDegreeList: [
  1259. { label: 0.1, value: 0.1 },
  1260. { label: 0.2, value: 0.2 },
  1261. { label: 0.3, value: 0.3 },
  1262. { label: 0.4, value: 0.4 },
  1263. { label: 0.5, value: 0.5 },
  1264. { label: 0.6, value: 0.6 },
  1265. { label: 0.7, value: 0.7 },
  1266. { label: 0.8, value: 0.8 },
  1267. { label: 0.9, value: 0.9 },
  1268. { label: 1.0, value: 1.0 },
  1269. ],
  1270. publicityList: [
  1271. { label: "公开", value: true },
  1272. { label: "非公开", value: false },
  1273. ],
  1274. answerTypes: [
  1275. { label: "文本", value: "DIVERSIFIED_TEXT" },
  1276. { label: "音频", value: "SINGLE_AUDIO" },
  1277. ],
  1278. matchingTypes: [
  1279. { label: "填词", value: 1 },
  1280. { label: "段落", value: 2 },
  1281. ],
  1282. matchingModes: [
  1283. { label: "单用", value: 1 },
  1284. { label: "复用", value: 2 },
  1285. ],
  1286. coursePropertyList: [],
  1287. coursePropertyId: "", //课程属性名
  1288. firstPropertyList: [], //一级属性集合
  1289. firstPropertyId: "", //一级属性id
  1290. secondPropertyList: [], //二级属性集合
  1291. secondPropertyId: "", //二级属性id
  1292. examRemark: "",
  1293. showQuestions: [],
  1294. showButtons: [],
  1295. showSubQuestions: {},
  1296. showSubButtons: {},
  1297. };
  1298. },
  1299. computed: {
  1300. ...mapState({
  1301. user: (state) => state.user,
  1302. }),
  1303. updatePorperty() {
  1304. return false;
  1305. },
  1306. answer() {
  1307. if (this.quesModel.questionType == "SINGLE_ANSWER_QUESTION") {
  1308. return this.singleRightAnswer;
  1309. } else if (this.quesModel.questionType == "MULTIPLE_ANSWER_QUESTION") {
  1310. var obj = this.multipleRightAnswer;
  1311. return obj.sort().toString();
  1312. }
  1313. return this.quesModel.quesAnswer;
  1314. },
  1315. enableAnswerPoint() {
  1316. return (
  1317. this.quesModel.questionType == "FILL_BLANK_QUESTION" ||
  1318. this.quesModel.questionType == "CLOZE" ||
  1319. this.quesModel.questionType == "BANKED_CLOZE"
  1320. );
  1321. },
  1322. },
  1323. created() {
  1324. let qt = sessionStorage.getItem("quesTagShow");
  1325. if (qt) {
  1326. this.quesTagShow = qt == "true";
  1327. }
  1328. let qa = sessionStorage.getItem("quesAnswerShow");
  1329. if (qa) {
  1330. this.quesAnswerShow = qa == "true";
  1331. }
  1332. document.getElementsByTagName("body")[0].style = "";
  1333. this.paperId = this.$route.params.id;
  1334. this.parentView = this.$route.params.parentView;
  1335. this.initPaper();
  1336. this.uploadAction = QUESTION_API + "/uploadRadio/" + this.paperId;
  1337. this.uploadHeaders = {
  1338. key: this.user.key,
  1339. token: this.user.token,
  1340. };
  1341. },
  1342. methods: {
  1343. showCheckDuplicate() {
  1344. if (this.paper.checkDuplicateStatus == "DISPOSED") {
  1345. this.showCheckDuplicateBtn = false;
  1346. } else if (this.paper.paperDetails) {
  1347. for (let paperDetilData of this.paper.paperDetails) {
  1348. if (paperDetilData.paperDetailUnits) {
  1349. for (let unit of paperDetilData.paperDetailUnits) {
  1350. if (unit.question.checkDuplicateStatus == "TO_BE_DISPOSE") {
  1351. this.showCheckDuplicateBtn = true;
  1352. return;
  1353. }
  1354. }
  1355. }
  1356. }
  1357. this.showCheckDuplicateBtn = false;
  1358. }
  1359. },
  1360. checkDuplicate() {
  1361. this.$router.push({
  1362. name: "check_duplicate_info",
  1363. query: {
  1364. basePaperId: this.paper.id,
  1365. from: "paper",
  1366. },
  1367. });
  1368. },
  1369. checkDuplicateQuestion(questionId) {
  1370. this.$router.push({
  1371. name: "check_duplicate_info",
  1372. query: {
  1373. quesId: questionId,
  1374. basePaperId: this.paper.id,
  1375. from: "paper",
  1376. },
  1377. });
  1378. },
  1379. toEditCard() {
  1380. this.$router.push({
  1381. name: "CardEdit",
  1382. params: {
  1383. idType: "paper",
  1384. paperOrCardId: this.paperId,
  1385. },
  1386. });
  1387. },
  1388. showAuditDialog() {
  1389. this.auditInfoDialog = true;
  1390. },
  1391. showBlueDialog() {
  1392. this.blueDialog = true;
  1393. },
  1394. showTypeDialog() {
  1395. this.typeDialog = true;
  1396. },
  1397. showBasicDialog() {
  1398. this.basicDialog = true;
  1399. },
  1400. quesAnswerShowHide() {
  1401. this.quesAnswerShow = !this.quesAnswerShow;
  1402. sessionStorage.setItem("quesAnswerShow", this.quesAnswerShow);
  1403. },
  1404. quesTagShowHide() {
  1405. this.quesTagShow = !this.quesTagShow;
  1406. sessionStorage.setItem("quesTagShow", this.quesTagShow);
  1407. },
  1408. isMatchingQuestion(questionType) {
  1409. if (
  1410. questionType == "PARAGRAPH_MATCHING" ||
  1411. questionType == "BANKED_CLOZE"
  1412. ) {
  1413. return true;
  1414. } else {
  1415. return false;
  1416. }
  1417. },
  1418. isNested(questionType) {
  1419. if (
  1420. questionType == "PARAGRAPH_MATCHING" ||
  1421. questionType == "BANKED_CLOZE" ||
  1422. questionType == "CLOZE" ||
  1423. questionType == "READING_COMPREHENSION" ||
  1424. questionType == "LISTENING_QUESTION"
  1425. ) {
  1426. return true;
  1427. } else {
  1428. return false;
  1429. }
  1430. },
  1431. otherSelect(optIndex, quesModel) {
  1432. return "该选项被第" + quesModel.optionsSelected[optIndex + 1] + "题选用";
  1433. },
  1434. isInOtherSelect(optIndex, quesModel) {
  1435. if (quesModel.parentQuesParam.matchingMode != 1) {
  1436. return false;
  1437. }
  1438. if (!quesModel.optionsSelected) {
  1439. return false;
  1440. }
  1441. if (!quesModel.optionsSelected[optIndex + 1]) {
  1442. return false;
  1443. }
  1444. if (
  1445. quesModel.optionsSelected[optIndex + 1].includes(quesModel.subNumber)
  1446. ) {
  1447. return false;
  1448. }
  1449. return true;
  1450. },
  1451. movePaperDetailUnitSub(unitid, subid, vector) {
  1452. let vectorStr = vector == "up" ? "上移" : "下移";
  1453. this.$alert("您确定" + vectorStr + "吗?", "提示", {
  1454. confirmButtonText: "确定",
  1455. callback: (action) => {
  1456. if (action == "confirm") {
  1457. this.loading = true;
  1458. this.$http
  1459. .put(
  1460. QUESTION_API +
  1461. "/paperDetailUnit/sub/" +
  1462. unitid +
  1463. "/" +
  1464. subid +
  1465. "/" +
  1466. vector
  1467. )
  1468. .then(() => {
  1469. this.initPaper();
  1470. this.loading = true;
  1471. this.$notify({
  1472. message: vectorStr + "成功",
  1473. type: "success",
  1474. });
  1475. this.loading = false;
  1476. });
  1477. }
  1478. },
  1479. });
  1480. },
  1481. showUnitSubDown(unit, subid) {
  1482. if (unit.question.subQuestions.length <= 1) {
  1483. return false;
  1484. }
  1485. if (
  1486. subid !=
  1487. unit.question.subQuestions[unit.question.subQuestions.length - 1].id
  1488. ) {
  1489. return true;
  1490. } else {
  1491. return false;
  1492. }
  1493. },
  1494. showUnitSubUp(unit, subid) {
  1495. if (unit.question.subQuestions.length <= 1) {
  1496. return false;
  1497. }
  1498. if (subid != unit.question.subQuestions[0].id) {
  1499. return true;
  1500. } else {
  1501. return false;
  1502. }
  1503. },
  1504. movePaperDetailUnit(detailId, unitid, vector) {
  1505. let vectorStr = vector == "up" ? "上移" : "下移";
  1506. this.$alert("您确定" + vectorStr + "吗?", "提示", {
  1507. confirmButtonText: "确定",
  1508. callback: (action) => {
  1509. if (action == "confirm") {
  1510. this.loading = true;
  1511. this.$http
  1512. .put(
  1513. QUESTION_API +
  1514. "/paperDetailUnit/" +
  1515. detailId +
  1516. "/" +
  1517. unitid +
  1518. "/" +
  1519. vector
  1520. )
  1521. .then(() => {
  1522. this.initPaper();
  1523. this.loading = true;
  1524. this.$notify({
  1525. message: vectorStr + "成功",
  1526. type: "success",
  1527. });
  1528. this.loading = false;
  1529. });
  1530. }
  1531. },
  1532. });
  1533. },
  1534. showUnitDown(detail, unitid) {
  1535. if (detail.paperDetailUnits.length <= 1) {
  1536. return false;
  1537. }
  1538. if (
  1539. unitid != detail.paperDetailUnits[detail.paperDetailUnits.length - 1].id
  1540. ) {
  1541. return true;
  1542. } else {
  1543. return false;
  1544. }
  1545. },
  1546. showUnitUp(detail, unitid) {
  1547. if (detail.paperDetailUnits.length <= 1) {
  1548. return false;
  1549. }
  1550. if (unitid != detail.paperDetailUnits[0].id) {
  1551. return true;
  1552. } else {
  1553. return false;
  1554. }
  1555. },
  1556. movePaperDetail(detail, vector) {
  1557. let vectorStr = vector == "up" ? "上移" : "下移";
  1558. this.$alert("您确定" + vectorStr + "吗?", "提示", {
  1559. confirmButtonText: "确定",
  1560. callback: (action) => {
  1561. if (action == "confirm") {
  1562. this.loading = true;
  1563. this.$http
  1564. .put(
  1565. QUESTION_API +
  1566. "/paperDetail/" +
  1567. this.paperId +
  1568. "/" +
  1569. detail.id +
  1570. "/" +
  1571. vector
  1572. )
  1573. .then(() => {
  1574. this.initPaper();
  1575. this.loading = true;
  1576. this.$notify({
  1577. message: vectorStr + "成功",
  1578. type: "success",
  1579. });
  1580. this.loading = false;
  1581. });
  1582. }
  1583. },
  1584. });
  1585. },
  1586. showUp(detail) {
  1587. if (this.paper.paperDetails.length <= 1) {
  1588. return false;
  1589. }
  1590. if (detail.id != this.paper.paperDetails[0].id) {
  1591. return true;
  1592. } else {
  1593. return false;
  1594. }
  1595. },
  1596. showDown(detail) {
  1597. if (this.paper.paperDetails.length <= 1) {
  1598. return false;
  1599. }
  1600. if (
  1601. detail.id !=
  1602. this.paper.paperDetails[this.paper.paperDetails.length - 1].id
  1603. ) {
  1604. return true;
  1605. } else {
  1606. return false;
  1607. }
  1608. },
  1609. downAnswerTemplate() {
  1610. var key = this.user.key;
  1611. var token = this.user.token;
  1612. window.open(
  1613. QUESTION_API + "/paper/answer/template?$key=" + key + "&$token=" + token
  1614. );
  1615. },
  1616. openAnswerDialog() {
  1617. this.checkResultAnswer = false;
  1618. this.isUploadAnswer = true;
  1619. if (document.getElementById("answerFile")) {
  1620. document.getElementById("answerFile").value = "";
  1621. }
  1622. this.answerFileName = "";
  1623. this.dialogAnswerFile = true;
  1624. this.answerFileList = [];
  1625. },
  1626. closeAnswerDialog() {
  1627. this.answerMessage = "";
  1628. this.dialogAnswerFile = this.uploadAnswerLoading;
  1629. },
  1630. selectAnswerFile() {
  1631. document.getElementById("answerFile").click();
  1632. },
  1633. answerFileChange() {
  1634. let fileList = document.getElementById("answerFile").files;
  1635. if (fileList.length == 0) {
  1636. this.answerFileName = "";
  1637. } else {
  1638. this.answerFileName = fileList[0].name;
  1639. }
  1640. },
  1641. uploadAnswerFile() {
  1642. this.answerMessage = "";
  1643. var fileList = document.getElementById("answerFile").files;
  1644. if (fileList.length == 0) {
  1645. this.answerMessage = "请选择文件!";
  1646. return;
  1647. }
  1648. let param = new FormData();
  1649. //循环添加到formData中
  1650. for (var i = 0; i < fileList.length; i++) {
  1651. var file = fileList[i];
  1652. param.append("dataFile", file, file.name);
  1653. }
  1654. let config = {
  1655. headers: { "Content-Type": "multipart/form-data" },
  1656. };
  1657. this.uploadAnswerLoading = true;
  1658. this.$http
  1659. .post(
  1660. QUESTION_API + "/paper/answer/import/" + this.paperId,
  1661. param,
  1662. config
  1663. )
  1664. .then(() => {
  1665. this.dialogAnswerFile = false;
  1666. this.uploadAnswerLoading = false;
  1667. this.checkResultAnswer = false;
  1668. this.isUploadAnswer = true;
  1669. document.getElementById("answerFile").value = "";
  1670. this.initPaper();
  1671. })
  1672. .catch((error) => {
  1673. this.answerMessage = error.response.data.desc;
  1674. document.getElementById("answerFile").value = "";
  1675. this.uploadAnswerLoading = false;
  1676. });
  1677. },
  1678. //隐藏大题下的所有小题
  1679. hideContent(index) {
  1680. this.showQuestions[index].is_show = false;
  1681. this.showButtons[index].up = false;
  1682. },
  1683. //展开大题下所有小题
  1684. showContent(index) {
  1685. this.showQuestions[index].is_show = true;
  1686. this.showButtons[index].up = true;
  1687. },
  1688. //隐藏大题下的所有小题
  1689. hideSubContent(index) {
  1690. this.showSubQuestions[index] = false;
  1691. this.showSubButtons[index] = false;
  1692. this.$forceUpdate();
  1693. },
  1694. //展开大题下所有小题
  1695. showSubContent(index) {
  1696. this.showSubQuestions[index] = true;
  1697. this.showSubButtons[index] = true;
  1698. this.$forceUpdate();
  1699. },
  1700. quesMouseOver(index) {
  1701. document.getElementById(index).style.visibility = "visible";
  1702. },
  1703. quesMouseOut(index) {
  1704. document.getElementById(index).style.visibility = "hidden";
  1705. },
  1706. selectQues(id) {
  1707. this.paperDetailId = id;
  1708. var courseId = this.paper.course.id;
  1709. this.$router.push({
  1710. path:
  1711. "/select_question/" +
  1712. this.paper.id +
  1713. "/" +
  1714. courseId +
  1715. "/" +
  1716. this.paperDetailId +
  1717. "/" +
  1718. this.parentView,
  1719. });
  1720. },
  1721. //打开编辑大题题目弹窗
  1722. openEditPaperDetail(paperDetail) {
  1723. this.paperDatailDialog = true;
  1724. this.editpaperDetail = Object.assign({}, paperDetail); //浅拷贝
  1725. },
  1726. //关闭编辑大题题目弹窗
  1727. closePaperDatailDialog() {
  1728. this.paperDatailDialog = false;
  1729. this.editpaperDetail = {};
  1730. },
  1731. //保存大题题目信息
  1732. savePaperDatail(editpaperDetail) {
  1733. this.detailLoading = true;
  1734. var paperId = this.paper.id;
  1735. this.$http
  1736. .post(QUESTION_API + "/updatePaperDetail/" + paperId, editpaperDetail)
  1737. .then(() => {
  1738. this.$notify({
  1739. message: "保存成功",
  1740. type: "success",
  1741. });
  1742. this.detailLoading = false;
  1743. this.closePaperDatailDialog();
  1744. this.initPaper();
  1745. });
  1746. },
  1747. //初始化试卷
  1748. initPaper() {
  1749. const scrollPosition =
  1750. document.documentElement.scrollTop || document.body.scrollTop;
  1751. this.loading = true;
  1752. this.paper = {
  1753. course: {
  1754. code: "",
  1755. name: "",
  1756. },
  1757. };
  1758. this.$http
  1759. .get(QUESTION_API + "/paper/" + this.paperId)
  1760. .then((response) => {
  1761. this.paper = response.data;
  1762. //查询所有课程属性名
  1763. this.initCourseProperty(this.paper.course.id);
  1764. //将所有小题分为公开和非公开
  1765. if (this.paper.paperDetails && this.paper.paperDetails.length > 0) {
  1766. let dindx = 0;
  1767. for (let paperDetil of this.paper.paperDetails) {
  1768. this.showQuestions.push({ is_show: true });
  1769. this.showButtons.push({ up: true });
  1770. paperDetil.pubCount = 0;
  1771. paperDetil.noPubCount = 0;
  1772. if (
  1773. paperDetil.paperDetailUnits &&
  1774. paperDetil.paperDetailUnits.length > 0
  1775. ) {
  1776. let uindx = 0;
  1777. for (let paperDetilUt of paperDetil.paperDetailUnits) {
  1778. this.showSubQuestions[dindx + "-" + uindx] = true;
  1779. this.showSubButtons[dindx + "-" + uindx] = true;
  1780. if (!this.isNested(paperDetilUt.question.questionType)) {
  1781. //非套题
  1782. if (paperDetilUt.question.publicity) {
  1783. paperDetil.pubCount = paperDetil.pubCount + 1;
  1784. } else {
  1785. paperDetil.noPubCount = paperDetil.noPubCount + 1;
  1786. }
  1787. } else {
  1788. //循环所有子题
  1789. for (let ques of paperDetilUt.question.subQuestions) {
  1790. if (ques.publicity) {
  1791. paperDetil.pubCount = paperDetil.pubCount + 1;
  1792. } else {
  1793. paperDetil.noPubCount = paperDetil.noPubCount + 1;
  1794. }
  1795. }
  1796. }
  1797. uindx++;
  1798. }
  1799. }
  1800. dindx++;
  1801. }
  1802. }
  1803. this.showCheckDuplicate();
  1804. setTimeout(() => {
  1805. document.documentElement.scrollTop = document.body.scrollTop =
  1806. scrollPosition;
  1807. console.log(scrollPosition);
  1808. }, 1000);
  1809. this.loading = false;
  1810. });
  1811. },
  1812. //查询所有课程属性名
  1813. initCourseProperty(courseId) {
  1814. this.$http
  1815. .get(QUESTION_API + "/courseProperty/enable?courseId=" + courseId)
  1816. .then((response) => {
  1817. this.coursePropertyList = response.data;
  1818. });
  1819. },
  1820. //删除大题
  1821. deletePaperDetail(paperDetailsId) {
  1822. //先判断大题下面是否还有小题
  1823. var count = 0;
  1824. for (var i = 0, imax = this.paper.paperDetails.length; i < imax; i++) {
  1825. if (paperDetailsId == this.paper.paperDetails[i].id) {
  1826. if (this.paper.paperDetails[i].paperDetailUnits) {
  1827. count += this.paper.paperDetails[i].paperDetailUnits.length;
  1828. break;
  1829. }
  1830. }
  1831. }
  1832. if (count == 0) {
  1833. this.$alert("您确定删除吗?", "提示", {
  1834. confirmButtonText: "确定",
  1835. callback: (action) => {
  1836. if (action == "confirm") {
  1837. this.loading = true;
  1838. this.$http
  1839. .delete(
  1840. QUESTION_API +
  1841. "/paperDetail/" +
  1842. this.paperId +
  1843. "/" +
  1844. paperDetailsId
  1845. )
  1846. .then(() => {
  1847. this.initPaper();
  1848. this.loading = true;
  1849. this.$notify({
  1850. message: "删除成功",
  1851. type: "success",
  1852. });
  1853. this.loading = false;
  1854. });
  1855. }
  1856. },
  1857. });
  1858. } else {
  1859. this.$alert("大题下还有小题,不可删除!", "提示", {
  1860. confirmButtonText: "确定",
  1861. callback: () => {},
  1862. });
  1863. }
  1864. },
  1865. quesShow(id) {
  1866. if (this.reduplicateGroup.length < 1) {
  1867. return true;
  1868. }
  1869. for (var i = 0, imax = this.reduplicateGroup.length; i < imax; i++) {
  1870. if (id == this.reduplicateGroup[i]) {
  1871. return true;
  1872. }
  1873. }
  1874. return false;
  1875. },
  1876. //编辑题目
  1877. editQues(paperDetailUnit, question) {
  1878. console.log("question:", question);
  1879. this.coursePropertyId = "";
  1880. this.firstPropertyId = "";
  1881. this.secondPropertyId = "";
  1882. this.editPaperDetailUnit = paperDetailUnit;
  1883. this.quesModel = JSON.parse(JSON.stringify(question)); //深拷贝
  1884. if (!this.quesModel.control) {
  1885. this.quesModel.control = {};
  1886. }
  1887. this.quesModel.score = paperDetailUnit.score;
  1888. //如果是套题下面的小题编辑 ( paperDetailUnit的类型是套题,question的类型不是套题)
  1889. if (
  1890. this.isNested(paperDetailUnit.questionType) &&
  1891. question.questionType != paperDetailUnit.questionType
  1892. ) {
  1893. for (var i = 0; i < paperDetailUnit.question.subQuestions.length; i++) {
  1894. if (
  1895. paperDetailUnit.question.subQuestions[i].id == this.quesModel.id
  1896. ) {
  1897. this.quesModel.score = paperDetailUnit.subScoreList[i];
  1898. this.quesModel.parentType = paperDetailUnit.questionType;
  1899. this.quesModel.optionsSelected = paperDetailUnit.optionsSelected;
  1900. this.quesModel.parentQuesParam = paperDetailUnit.question.quesParam;
  1901. break;
  1902. }
  1903. }
  1904. }
  1905. if (isEmptyStr(this.quesModel.answerType)) {
  1906. this.quesModel.answerType = "DIVERSIFIED_TEXT";
  1907. }
  1908. if (this.quesModel.questionType === "BOOL_ANSWER_QUESTION") {
  1909. this.quesAnswer = this.quesModel.quesAnswer === "true" ? 1 : 0;
  1910. } else if (this.quesModel.questionType === "TEXT_ANSWER_QUESTION") {
  1911. const answer = JSON.parse(this.quesModel.quesAnswer);
  1912. this.quesAnswer = answer[0];
  1913. } else if (this.quesModel.questionType == "FILL_BLANK_QUESTION") {
  1914. const answer = JSON.parse(this.quesModel.quesAnswer);
  1915. this.quesAnswer = answer;
  1916. } else {
  1917. this.quesAnswer = null;
  1918. }
  1919. this.prevAnswerPointCount = this.getAnswerPointCount(
  1920. this.quesModel.quesBody
  1921. );
  1922. this.assignAnswers(); //给singleRightAnswer或multipleRightAnswer赋值
  1923. this.openQuesDialog();
  1924. },
  1925. //给singleRightAnswer和multipleRightAnswer赋值
  1926. assignAnswers() {
  1927. if (this.quesModel.quesOptions && this.quesModel.quesOptions.length > 0) {
  1928. this.singleRightAnswer = "";
  1929. this.multipleRightAnswer = [];
  1930. for (let i = 0; i < this.quesModel.quesOptions.length; i++) {
  1931. let option = this.quesModel.quesOptions[i];
  1932. if (
  1933. this.quesModel.questionType == "SINGLE_ANSWER_QUESTION" &&
  1934. option.isCorrect == 1
  1935. ) {
  1936. this.singleRightAnswer = String.fromCharCode(65 + i);
  1937. }
  1938. if (
  1939. this.quesModel.questionType == "MULTIPLE_ANSWER_QUESTION" &&
  1940. option.isCorrect == 1
  1941. ) {
  1942. this.multipleRightAnswer.push(String.fromCharCode(65 + i));
  1943. }
  1944. }
  1945. }
  1946. },
  1947. boolAnswerChange(val) {
  1948. this.quesModel.quesAnswer = val ? "true" : "false";
  1949. },
  1950. textAnswerChange(val) {
  1951. this.quesModel.quesAnswer = JSON.stringify([val]);
  1952. },
  1953. resetNumberAndSaveAnswerPoints(answer) {
  1954. this.quesAnswer = answer.map((element, index) => {
  1955. element.index = index + 1;
  1956. return element;
  1957. });
  1958. this.quesModel.quesAnswer = JSON.stringify(this.quesAnswer);
  1959. },
  1960. updateAnswerPoint(index, value) {
  1961. // console.log(index, this.question.answer[index]);
  1962. this.quesAnswer[index] = { ...value };
  1963. this.resetNumberAndSaveAnswerPoints(this.quesAnswer);
  1964. },
  1965. getAnswerPointCount(bodyJson) {
  1966. let count = 0;
  1967. bodyJson.sections.forEach((section) => {
  1968. section.blocks.forEach((block) => {
  1969. if (block.type === "cloze") count++;
  1970. });
  1971. });
  1972. return count;
  1973. },
  1974. quesBodyChange(quesBodyJson) {
  1975. this.quesModel.quesBody = quesBodyJson;
  1976. if (this.quesModel.questionType == "FILL_BLANK_QUESTION") {
  1977. let curPonitCount = this.getAnswerPointCount(quesBodyJson);
  1978. if (curPonitCount === this.prevAnswerPointCount) return;
  1979. this.prevAnswerPointCount = curPonitCount;
  1980. let newAnswer = [];
  1981. for (let i = 0; i < curPonitCount; i++) {
  1982. newAnswer.push(this.quesAnswer[i] || { sections: [] });
  1983. }
  1984. this.resetNumberAndSaveAnswerPoints(newAnswer);
  1985. }
  1986. },
  1987. //打开修改试题编辑框
  1988. openQuesDialog() {
  1989. this.quesDialog = true;
  1990. },
  1991. //关闭试题编辑框
  1992. closeQuesDialog() {
  1993. this.quesDialog = false;
  1994. this.quesModel = {};
  1995. },
  1996. //删除属性
  1997. handleClose(tag) {
  1998. this.quesModel.quesProperties.splice(
  1999. this.quesModel.quesProperties.indexOf(tag),
  2000. 1
  2001. );
  2002. },
  2003. //查询一级属性
  2004. searchFirst() {
  2005. this.firstPropertyId = "";
  2006. this.secondPropertyId = "";
  2007. this.secondPropertyList = [];
  2008. if (this.coursePropertyId) {
  2009. for (let courseProperty of this.coursePropertyList) {
  2010. if (courseProperty.id == this.coursePropertyId) {
  2011. this.$http
  2012. .get(QUESTION_API + "/property/first/" + courseProperty.id)
  2013. .then((response) => {
  2014. this.firstPropertyList = response.data;
  2015. });
  2016. }
  2017. }
  2018. }
  2019. },
  2020. //查询二级属性
  2021. searchSecond() {
  2022. this.secondPropertyId = "";
  2023. if (this.firstPropertyId) {
  2024. this.$http
  2025. .get(QUESTION_API + "/property/second/" + this.firstPropertyId)
  2026. .then((response) => {
  2027. this.secondPropertyList = response.data;
  2028. });
  2029. }
  2030. },
  2031. //新增属性
  2032. insertProperty() {
  2033. if (!this.checkInsertPro()) {
  2034. return false;
  2035. }
  2036. var quesProperty = {
  2037. id: null,
  2038. courseProperty: {},
  2039. firstProperty: {},
  2040. secondProperty: {},
  2041. };
  2042. if (
  2043. this.quesModel.quesProperties == null ||
  2044. this.quesModel.quesProperties.length == 0
  2045. ) {
  2046. this.quesModel.quesProperties = [];
  2047. }
  2048. for (let quesPro of this.quesModel.quesProperties) {
  2049. if (quesPro.id == quesProperty.id) {
  2050. this.$notify({
  2051. message: "该属性已存在,请重新选择",
  2052. type: "error",
  2053. });
  2054. return false;
  2055. }
  2056. }
  2057. for (let courseProperty of this.coursePropertyList) {
  2058. if (courseProperty.id == this.coursePropertyId) {
  2059. quesProperty.courseProperty = courseProperty;
  2060. }
  2061. }
  2062. //取到一级属性对象
  2063. for (let property of this.firstPropertyList) {
  2064. if (property.id == this.firstPropertyId) {
  2065. quesProperty.firstProperty = property;
  2066. }
  2067. }
  2068. //判断是否有二级属性
  2069. if (
  2070. this.secondPropertyList != undefined &&
  2071. this.secondPropertyList.length > 0
  2072. ) {
  2073. if (!this.secondPropertyId) {
  2074. this.$notify({
  2075. message: "请选择二级属性",
  2076. type: "error",
  2077. });
  2078. return false;
  2079. }
  2080. }
  2081. //取到二级属性对象
  2082. for (let property of this.secondPropertyList) {
  2083. if (property.id == this.secondPropertyId) {
  2084. quesProperty.secondProperty = property;
  2085. }
  2086. }
  2087. this.quesModel.quesProperties.push(quesProperty);
  2088. this.quesModel = Object.assign({}, this.quesModel);
  2089. //清空下拉框
  2090. this.coursePropertyId = "";
  2091. this.firstPropertyId = "";
  2092. this.secondPropertyId = "";
  2093. this.firstPropertyList = [];
  2094. this.secondPropertyList = [];
  2095. },
  2096. //新增属性验证
  2097. checkInsertPro() {
  2098. if (!this.coursePropertyId) {
  2099. this.$notify({
  2100. message: "请选择属性",
  2101. type: "error",
  2102. });
  2103. return false;
  2104. }
  2105. if (!this.firstPropertyId) {
  2106. this.$notify({
  2107. message: "请选择一级属性",
  2108. type: "error",
  2109. });
  2110. return false;
  2111. }
  2112. return true;
  2113. },
  2114. //删除选项
  2115. removeQuesOption(option) {
  2116. if (this.quesModel.quesOptions.length == 1) {
  2117. this.$notify({
  2118. message: "不能删除最后一个选项",
  2119. type: "error",
  2120. });
  2121. return;
  2122. }
  2123. this.singleRightAnswer = "";
  2124. this.multipleRightAnswer = [];
  2125. let index = this.quesModel.quesOptions.indexOf(option);
  2126. if (index !== -1) {
  2127. this.quesModel.quesOptions.splice(index, 1);
  2128. }
  2129. if (this.quesModel.quesOptions.length > 0) {
  2130. for (var i = 0; i < this.quesModel.quesOptions.length; i++) {
  2131. var quesOption = this.quesModel.quesOptions[i];
  2132. quesOption["number"] = i + 1;
  2133. if (quesOption.isCorrect == 1) {
  2134. var answerOrderNum = String.fromCharCode(65 + i);
  2135. if (this.quesModel.questionType == "SINGLE_ANSWER_QUESTION") {
  2136. this.singleRightAnswer = answerOrderNum;
  2137. }
  2138. if (this.quesModel.questionType == "MULTIPLE_ANSWER_QUESTION") {
  2139. this.multipleRightAnswer.push(answerOrderNum);
  2140. }
  2141. }
  2142. }
  2143. }
  2144. },
  2145. //新增选项
  2146. addQuesOption() {
  2147. this.quesModel.quesOptions.push({
  2148. number: "",
  2149. optionBody: "",
  2150. isCorrect: "",
  2151. });
  2152. for (var i = 0; i < this.quesModel.quesOptions.length; i++) {
  2153. this.quesModel.quesOptions[i]["number"] = i + 1;
  2154. }
  2155. },
  2156. savePaperDetailUnit() {
  2157. //跟新难度值
  2158. if (this.quesModel.difficultyDegree < 0.4) {
  2159. this.quesModel.difficulty = "难";
  2160. } else if (
  2161. this.quesModel.difficultyDegree > 0.3 &&
  2162. this.quesModel.difficultyDegree < 0.8
  2163. ) {
  2164. this.quesModel.difficulty = "中";
  2165. } else {
  2166. this.quesModel.difficulty = "易";
  2167. }
  2168. this.setRightAnswer();
  2169. if (/^\d+(?=\.{0,1}\d+$|$)/.test(this.quesModel.score)) {
  2170. console.log("正确");
  2171. } else {
  2172. this.$notify({
  2173. message: "分数只能为正数",
  2174. type: "error",
  2175. });
  2176. return;
  2177. }
  2178. if (this.enableAnswerPoint) {
  2179. const pointCount = this.getAnswerPointCount(this.quesModel.quesBody);
  2180. if (!pointCount) {
  2181. this.$notify({
  2182. message: "请插入答题点",
  2183. type: "error",
  2184. });
  2185. return;
  2186. }
  2187. }
  2188. if (this.paper.paperType == "GENERATE") {
  2189. this.$confirm(
  2190. "试题内容修改,会影响所有关联试卷,是否确定进行?",
  2191. "提示",
  2192. {
  2193. confirmButtonText: "确定",
  2194. cancelButtonText: "取消",
  2195. type: "warning",
  2196. }
  2197. ).then(() => {
  2198. this.submitPaperDetailUnit();
  2199. });
  2200. } else {
  2201. this.submitPaperDetailUnit();
  2202. }
  2203. },
  2204. submitPaperDetailUnit() {
  2205. let paperDetailUnitExp = {
  2206. id: this.editPaperDetailUnit.id,
  2207. question: this.quesModel,
  2208. score: this.quesModel.score,
  2209. };
  2210. if (
  2211. (this.quesModel.questionType == "SINGLE_ANSWER_QUESTION" ||
  2212. this.quesModel.questionType == "MULTIPLE_ANSWER_QUESTION") &&
  2213. this.quesModel.quesOptions.length == 0
  2214. ) {
  2215. this.$confirm("无选项将删除该试题, 是否继续?", "提示", {
  2216. confirmButtonText: "确定",
  2217. cancelButtonText: "取消",
  2218. type: "warning",
  2219. }).then(() => {
  2220. this.dialogLoading = true;
  2221. this.$http
  2222. .delete(
  2223. QUESTION_API +
  2224. "/paper/deleteQuestion/" +
  2225. this.editPaperDetailUnit.id +
  2226. "/" +
  2227. this.quesModel.id
  2228. )
  2229. .then((response) => {
  2230. if (response.data.length > 0) {
  2231. var deleteInfo =
  2232. "该试题被试卷:" +
  2233. response.data.join(" , ") +
  2234. "使用,不能删除";
  2235. this.$notify({
  2236. message: deleteInfo,
  2237. type: "error",
  2238. });
  2239. } else {
  2240. this.$notify({
  2241. message: "保存成功",
  2242. type: "success",
  2243. });
  2244. }
  2245. this.dialogLoading = false;
  2246. });
  2247. });
  2248. } else {
  2249. this.dialogLoading = true;
  2250. //校验音频重复
  2251. let audiomap = new Map();
  2252. let regex = new RegExp(
  2253. '<a id="[^<>]+" name="([^<>]+\\.mp3)"></a>',
  2254. "ig"
  2255. );
  2256. let ret = "";
  2257. let quesBodyStr = paperDetailUnitExp.question.quesBody;
  2258. if (quesBodyStr) {
  2259. while ((ret = regex.exec(quesBodyStr))) {
  2260. if (audiomap.get(ret[1])) {
  2261. this.dialogLoading = false;
  2262. this.$notify({
  2263. type: "error",
  2264. message: "题干中存在相同的音频文件",
  2265. });
  2266. return;
  2267. } else {
  2268. audiomap.set(ret[1], ret[1]);
  2269. }
  2270. }
  2271. }
  2272. let quesAnswerStr = paperDetailUnitExp.question.quesAnswer;
  2273. if (quesAnswerStr) {
  2274. while ((ret = regex.exec(quesAnswerStr))) {
  2275. if (audiomap.get(ret[1])) {
  2276. this.dialogLoading = false;
  2277. this.$notify({
  2278. type: "error",
  2279. message: "答案中存在相同的音频文件",
  2280. });
  2281. return;
  2282. } else {
  2283. audiomap.set(ret[1], ret[1]);
  2284. }
  2285. }
  2286. }
  2287. let quesOptions = paperDetailUnitExp.question.quesOptions;
  2288. if (quesOptions) {
  2289. for (let i = 0; i < quesOptions.length; i++) {
  2290. let quesOptionStr = quesOptions[i].optionBody;
  2291. while ((ret = regex.exec(quesOptionStr))) {
  2292. if (audiomap.get(ret[1])) {
  2293. this.dialogLoading = false;
  2294. this.$notify({
  2295. type: "error",
  2296. message: "选项中存在相同的音频文件",
  2297. });
  2298. return;
  2299. } else {
  2300. audiomap.set(ret[1], ret[1]);
  2301. }
  2302. }
  2303. }
  2304. }
  2305. // paperDetailUnitExp.question.quesAnswer = this.answer;
  2306. this.$http
  2307. .put(QUESTION_API + "/paperDetailUnit", paperDetailUnitExp)
  2308. .then(() => {
  2309. this.$notify({
  2310. message: "保存成功",
  2311. type: "success",
  2312. });
  2313. this.dialogLoading = false;
  2314. this.closeQuesDialog();
  2315. this.initPaper();
  2316. })
  2317. .catch((err) => {
  2318. this.dialogLoading = false;
  2319. this.$notify({
  2320. type: "error",
  2321. message: err.response.data.desc,
  2322. });
  2323. });
  2324. }
  2325. },
  2326. //在正确的option上设置isCorrect=1
  2327. setRightAnswer() {
  2328. if (
  2329. !this.quesModel.quesOptions ||
  2330. this.quesModel.quesOptions.length == 0
  2331. ) {
  2332. return false;
  2333. }
  2334. let quesAnswer = [];
  2335. for (var i = 0; i < this.quesModel.quesOptions.length; i++) {
  2336. var option = this.quesModel.quesOptions[i];
  2337. var answerOrderNum = String.fromCharCode(65 + i);
  2338. if (this.quesModel.questionType == "SINGLE_ANSWER_QUESTION") {
  2339. option["isCorrect"] =
  2340. answerOrderNum == this.singleRightAnswer ? 1 : 0;
  2341. }
  2342. if (this.quesModel.questionType == "MULTIPLE_ANSWER_QUESTION") {
  2343. option["isCorrect"] =
  2344. this.multipleRightAnswer.indexOf(answerOrderNum) > -1 ? 1 : 0;
  2345. }
  2346. if (option["isCorrect"]) quesAnswer.push(i + 1);
  2347. }
  2348. this.quesModel.quesAnswer = JSON.stringify(quesAnswer);
  2349. },
  2350. //删除试题
  2351. deleteQues(paperDetailUnit) {
  2352. let paperDetailUnitId = paperDetailUnit.id;
  2353. if (this.paper.paperType == "GENERATE") {
  2354. this.deleteQues01(paperDetailUnitId);
  2355. } else {
  2356. let questionId = paperDetailUnit.question.id;
  2357. this.deleteQues02(questionId, paperDetailUnitId);
  2358. }
  2359. },
  2360. deleteQues01(paperDetailUnitId) {
  2361. this.$alert("您确定删除吗?", "提示", {
  2362. confirmButtonText: "确定",
  2363. callback: (action) => {
  2364. if (action == "confirm") {
  2365. this.loading = true;
  2366. this.$http
  2367. .delete(QUESTION_API + "/paperDetailUnit/" + paperDetailUnitId)
  2368. .then(() => {
  2369. this.initPaper();
  2370. this.reduplicateGroup = [];
  2371. this.loading = true;
  2372. this.$notify({
  2373. message: "删除成功",
  2374. type: "success",
  2375. });
  2376. this.loading = false;
  2377. });
  2378. }
  2379. },
  2380. });
  2381. },
  2382. deleteQues02(questionId, paperDetailUnitId) {
  2383. this.$alert("您确定删除吗?", "提示", {
  2384. confirmButtonText: "确定",
  2385. callback: (action) => {
  2386. if (action == "confirm") {
  2387. this.loading = true;
  2388. this.$http
  2389. .delete(
  2390. QUESTION_API +
  2391. "/paper/deleteQuestion/" +
  2392. paperDetailUnitId +
  2393. "/" +
  2394. questionId
  2395. )
  2396. .then((response) => {
  2397. if (response.data.length > 0) {
  2398. var deleteInfo =
  2399. "该试题被试卷:" +
  2400. response.data.join(" , ") +
  2401. "使用,不能删除";
  2402. this.$notify({
  2403. message: deleteInfo,
  2404. type: "error",
  2405. });
  2406. } else {
  2407. this.initPaper();
  2408. this.reduplicateGroup = [];
  2409. this.loading = true;
  2410. this.$notify({
  2411. message: "保存成功",
  2412. type: "success",
  2413. });
  2414. }
  2415. this.loading = false;
  2416. });
  2417. }
  2418. },
  2419. });
  2420. },
  2421. exportPaperAnswer() {
  2422. var key = this.user.key;
  2423. var token = this.user.token;
  2424. window.open(
  2425. QUESTION_API +
  2426. "/paper/answer/export/" +
  2427. this.paperId +
  2428. "?$key=" +
  2429. key +
  2430. "&$token=" +
  2431. token
  2432. );
  2433. },
  2434. getSubQuesEditId(paperDetailUnit, subQuestion) {
  2435. return paperDetailUnit.question.id + "_" + subQuestion.subNumber;
  2436. },
  2437. //打开考试说明编辑框
  2438. openEditExamPaperRemark() {
  2439. if (this.paper.examRemark) {
  2440. this.examRemark = this.paper.examRemark;
  2441. } else {
  2442. this.examRemark = "";
  2443. }
  2444. this.paperRemarkDialog = true;
  2445. },
  2446. //保存考试说明
  2447. savePaperRemark() {
  2448. this.paper.examRemark = this.examRemark;
  2449. this.savePaper();
  2450. this.paperRemarkDialog = false;
  2451. },
  2452. //关闭考试说明编辑框
  2453. closPaperRemark() {
  2454. this.examRemark = "";
  2455. this.paperRemarkDialog = false;
  2456. },
  2457. //保存试卷
  2458. savePaper() {
  2459. this.loading = true;
  2460. this.$http
  2461. .put(QUESTION_API + "/paper", this.paper)
  2462. .then(() => {
  2463. this.$notify({
  2464. message: "保存成功",
  2465. type: "success",
  2466. });
  2467. this.loading = false;
  2468. this.initPaper();
  2469. })
  2470. .catch((error) => {
  2471. this.loading = false;
  2472. this.$notify({
  2473. type: "error",
  2474. message: error.response.data.desc,
  2475. });
  2476. });
  2477. },
  2478. //删除试卷
  2479. deletePaper(id) {
  2480. this.$confirm("确认删除试卷吗?", "提示", {
  2481. type: "warning",
  2482. }).then(() => {
  2483. this.loading = true;
  2484. this.$http.delete(QUESTION_API + "/paper/" + id).then(
  2485. () => {
  2486. this.$notify({
  2487. message: "删除成功",
  2488. type: "success",
  2489. });
  2490. this.back();
  2491. },
  2492. (error) => {
  2493. this.$notify({
  2494. message: error.response.data.desc,
  2495. type: "error",
  2496. title: "错误",
  2497. });
  2498. this.loading = false;
  2499. }
  2500. );
  2501. });
  2502. },
  2503. //打开上传音频弹框
  2504. openDialog() {
  2505. this.checkResult = false;
  2506. this.isUpload = true;
  2507. if (document.getElementById("radioFile")) {
  2508. document.getElementById("radioFile").value = "";
  2509. }
  2510. this.audioFileName = "";
  2511. this.dialogRadioFile = true;
  2512. this.fileList = [];
  2513. },
  2514. //关闭音频弹框
  2515. closeAudioDialog() {
  2516. this.dialogRadioFile = this.uploadAudioLoading;
  2517. },
  2518. //返回
  2519. back() {
  2520. if (sessionStorage.getItem("question_back") == "true") {
  2521. this.$router.push({
  2522. path: "/questions/" + this.parentView + "/0",
  2523. });
  2524. } else {
  2525. this.$router.push({
  2526. path: "/questions/" + this.parentView + "/1",
  2527. });
  2528. }
  2529. },
  2530. paperDetailShow(paperDetail) {
  2531. if (this.reduplicateGroup.length == 0) {
  2532. return true;
  2533. }
  2534. let paperDetailUnits = paperDetail.paperDetailUnits;
  2535. for (let i = 0, imax = paperDetailUnits.length; i < imax; i++) {
  2536. for (var j = 0, jmax = this.reduplicateGroup.length; j < jmax; j++) {
  2537. if (paperDetailUnits[i].id == this.reduplicateGroup[j]) {
  2538. return true;
  2539. }
  2540. }
  2541. }
  2542. return false;
  2543. },
  2544. //上传文件检查
  2545. checkFile() {
  2546. this.fileNameList = [];
  2547. //读取选取的文件夹里面的文件
  2548. this.checkResult = true;
  2549. var files = document.getElementById("radioFile").files;
  2550. if (files.length == 0) {
  2551. this.message = "请选择音频文件夹!";
  2552. return;
  2553. }
  2554. var size = 0;
  2555. var isGo = false;
  2556. //取到所有文件的文件名
  2557. for (var i = 0; i < files.length; i++) {
  2558. this.fileNameList.push(files[i].name);
  2559. if (files[i].size > 5 * 1024 * 1024) {
  2560. isGo = true;
  2561. break;
  2562. }
  2563. size = files[i].size + size;
  2564. }
  2565. if (isGo) {
  2566. this.message = "上传单个文件不能超过5M";
  2567. this.isUpload = true;
  2568. }
  2569. if (size > 50 * 1024 * 1024) {
  2570. this.message = "上传文件总和不能超过50M";
  2571. this.isUpload = true;
  2572. return;
  2573. }
  2574. this.$http
  2575. .post(
  2576. QUESTION_API + "/checkRadioFile/" + this.paperId,
  2577. this.fileNameList
  2578. )
  2579. .then((response) => {
  2580. console.log("response:", response);
  2581. this.message = response.data.errorMsg;
  2582. if (this.message == "OK") {
  2583. this.message = "OK!";
  2584. this.isUpload = false;
  2585. } else {
  2586. this.isUpload = true;
  2587. }
  2588. })
  2589. .catch((error) => {
  2590. console.log(error);
  2591. });
  2592. },
  2593. // 音频文件
  2594. selectAudioFile() {
  2595. document.getElementById("radioFile").click();
  2596. },
  2597. audioFileChange() {
  2598. let fileList = document.getElementById("radioFile").files;
  2599. if (fileList.length == 0) {
  2600. this.audioFileName = "";
  2601. } else {
  2602. let names = [];
  2603. for (var i = 0; i < fileList.length; i++) {
  2604. var file = fileList[i];
  2605. names.push(file.name);
  2606. }
  2607. this.audioFileName = names.join();
  2608. }
  2609. },
  2610. uploadAudioFile() {
  2611. let param = new FormData();
  2612. var fileList = document.getElementById("radioFile").files;
  2613. //循环添加到formData中
  2614. for (var i = 0; i < fileList.length; i++) {
  2615. var file = fileList[i];
  2616. param.append("files", file, file.name);
  2617. }
  2618. let config = {
  2619. headers: { "Content-Type": "multipart/form-data" },
  2620. };
  2621. this.$http
  2622. .post(QUESTION_API + "/uploadRadio/" + this.paperId, param, config)
  2623. .then(() => {
  2624. this.dialogRadioFile = false;
  2625. this.uploadAudioLoading = false;
  2626. this.checkResult = false;
  2627. this.isUpload = true;
  2628. document.getElementById("radioFile").value = "";
  2629. this.initPaper();
  2630. })
  2631. .catch((error) => {
  2632. this.message = error.response.data.desc;
  2633. this.uploadAudioLoading = false;
  2634. });
  2635. },
  2636. },
  2637. };
  2638. </script>