index.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603
  1. <template>
  2. <div
  3. class="header-separate"
  4. v-if="judgeAnswer == 'standardAnswer' ? (userError ? true : false) : true"
  5. >
  6. <AnswerTitle :judgeAnswer="judgeAnswer" />
  7. <table>
  8. <colgroup>
  9. <col
  10. v-for="(item, i) in curQue.tableData.colsConfig.width"
  11. :key="`col-${i}`"
  12. :style="{ width: `${item.val}px` }"
  13. />
  14. </colgroup>
  15. <thead>
  16. <tr>
  17. <th
  18. v-for="({ text, english, type }, i) in curQue.tableData.headers"
  19. :key="`th-${i}`"
  20. >
  21. <div class="thead-content" :style="theadStyle">
  22. <span class="chs">{{ text }}</span>
  23. <span :class="[type === 'english' ? 'english' : 'pinyin']">
  24. {{ english }}
  25. </span>
  26. </div>
  27. </th>
  28. </tr>
  29. </thead>
  30. <tbody
  31. :style="{
  32. 'text-align': `${curQue.textAlign}`,
  33. }"
  34. >
  35. <tr v-for="(row, i) in curQue.tableData.body" :key="`tr-${i}`">
  36. <template v-for="(col, j) in row.content">
  37. <td
  38. v-if="tdIsShow(i, j)"
  39. :key="`td-${j}`"
  40. :colspan="col.colspan"
  41. :rowspan="col.rowspan"
  42. :class="[
  43. { underline: col.isUnderline },
  44. `${curQue.firstColAligin === 'center' ? 'col-center' : ''}`,
  45. judgeAnswer == 'standardAnswer' ? 'correct' : '',
  46. judgeAnswer == 'studentAnswer' || judgeAnswer == 'userAnswer'
  47. ? curQue.Bookanswer[i].content[j].userAnswerJudge ==
  48. '[JUDGE##T##JUDGE]'
  49. ? 'correct'
  50. : 'error'
  51. : '',
  52. ]"
  53. :style="{
  54. 'background-color': `${col.background}`,
  55. display: tdHeaderIsNone(i, j),
  56. }"
  57. >
  58. <div class="cell-wrap">
  59. <template v-if="col.type === 'content'">
  60. <span v-if="col.text.length > 0" class="content">
  61. {{ col.text }}
  62. </span>
  63. <template v-else>
  64. <el-input
  65. v-model="
  66. judgeAnswer == 'standardAnswer'
  67. ? col.answer
  68. : curQue.Bookanswer[i].content[j].answer
  69. "
  70. type="textarea"
  71. :placeholder="`${isAnswerMode ? '' : '输入'}`"
  72. :disabled="isAnswerMode"
  73. :autosize="{ minRows: 1, maxRows: 6 }"
  74. @input="enterAnswer(i, j, 'input')"
  75. />
  76. </template>
  77. </template>
  78. <div
  79. v-else-if="col.type === 'pinyin'"
  80. class="sentence"
  81. :style="pinyinStyle"
  82. >
  83. <div>
  84. <span
  85. v-for="({ pinyin, chs }, k) in col.sentence_data
  86. .wordsList"
  87. :key="`${
  88. curQue.pinyinPosition === 'top' ||
  89. curQue.pinyinPosition === 'left'
  90. ? 'pinyin'
  91. : 'chs'
  92. }-${k}`"
  93. :class="[
  94. `${
  95. curQue.pinyinPosition === 'top' ||
  96. curQue.pinyinPosition === 'left'
  97. ? 'pinyin'
  98. : 'chs'
  99. }`,
  100. ]"
  101. >
  102. {{
  103. curQue.pinyinPosition === "top" ||
  104. curQue.pinyinPosition == "left"
  105. ? pinyin
  106. : chs
  107. }}
  108. </span>
  109. </div>
  110. <div>
  111. <span
  112. v-for="({ pinyin, chs }, k) in col.sentence_data
  113. .wordsList"
  114. :key="`${
  115. curQue.pinyinPosition === 'top' ||
  116. curQue.pinyinPosition === 'left'
  117. ? 'chs'
  118. : 'pinyin'
  119. }-${k}`"
  120. :class="[
  121. `${
  122. curQue.pinyinPosition === 'top' ||
  123. curQue.pinyinPosition === 'left'
  124. ? 'chs'
  125. : 'pinyin'
  126. }`,
  127. ]"
  128. >
  129. {{
  130. curQue.pinyinPosition === "top" ||
  131. curQue.pinyinPosition == "left"
  132. ? chs
  133. : pinyin
  134. }}
  135. </span>
  136. </div>
  137. </div>
  138. <div
  139. v-else-if="col.type === 'prePinyin'"
  140. :style="{
  141. 'flex-direction':
  142. col.prefixOrSuffix === 'prefix' ? 'row' : 'row-reverse',
  143. }"
  144. class="pre-pinyin"
  145. >
  146. <span>{{ col.message }}</span>
  147. <div
  148. class="right-pinyin"
  149. :style="{
  150. 'grid-template-columns': `repeat(${col.sentence_data.wordsList.length}, auto)`,
  151. }"
  152. >
  153. <span
  154. v-for="({ pinyin }, k) in col.sentence_data.wordsList"
  155. :key="`pre-pinyin-${k}`"
  156. class="pinyin"
  157. >
  158. {{ pinyin }}
  159. </span>
  160. <span
  161. v-for="({ pinyin, chs }, k) in col.sentence_data
  162. .wordsList"
  163. :key="`pre-chs-${k}`"
  164. class="chs"
  165. >
  166. {{ chs }}
  167. </span>
  168. </div>
  169. </div>
  170. <CrossTick
  171. v-if="col.isCross"
  172. :index="i"
  173. :indexs="j"
  174. :data="
  175. judgeAnswer == 'standardAnswer'
  176. ? col
  177. : curQue.Bookanswer[i].content[j]
  178. "
  179. :isAnswerMode="isAnswerMode"
  180. @enterAnswer="enterAnswer"
  181. />
  182. </div>
  183. </td>
  184. </template>
  185. </tr>
  186. </tbody>
  187. </table>
  188. </div>
  189. </template>
  190. <script>
  191. import CrossTick from "./CrossTick.vue";
  192. import AnswerTitle from "../../preview/components/AnswerTitle.vue";
  193. export default {
  194. components: { CrossTick, AnswerTitle },
  195. props: {
  196. curQue: {
  197. type: Object,
  198. required: true,
  199. },
  200. themeColor: {
  201. type: String,
  202. required: true,
  203. },
  204. judgeAnswer: {
  205. type: String,
  206. },
  207. },
  208. data() {
  209. return {
  210. isAnswerMode: false,
  211. userError: false,
  212. };
  213. },
  214. computed: {
  215. theadStyle() {
  216. const hp = this.curQue.headerEnglishPosition;
  217. if (hp === "top") {
  218. return {
  219. "flex-direction": "column-reverse",
  220. };
  221. }
  222. if (hp === "right") {
  223. return {
  224. "flex-direction": "row",
  225. "column-gap": "8px",
  226. };
  227. }
  228. if (hp === "bottom") {
  229. return {
  230. "flex-direction": "column",
  231. };
  232. }
  233. if (hp === "left") {
  234. return {
  235. "flex-direction": "row-reverse",
  236. "column-gap": "8px",
  237. };
  238. }
  239. },
  240. pinyinStyle() {
  241. let pyPos = this.curQue.pinyinPosition;
  242. if (pyPos === "left") {
  243. return {
  244. "column-gap": "16px",
  245. };
  246. }
  247. if (pyPos === "top") {
  248. return {
  249. "flex-direction": "column",
  250. };
  251. }
  252. if (pyPos === "right") {
  253. return {
  254. "column-gap": "16px",
  255. };
  256. }
  257. if (pyPos === "bottom") {
  258. return {
  259. "flex-direction": "column",
  260. };
  261. }
  262. },
  263. },
  264. created() {
  265. if (this.judgeAnswer) {
  266. this.isAnswerMode = true;
  267. }
  268. if (!this.curQue.Bookanswer) {
  269. let arr = [];
  270. this.curQue.tableData.body.forEach((item, i) => {
  271. arr.push({
  272. content: [],
  273. });
  274. item.content.forEach((items) => {
  275. arr[i].content.push({
  276. answer: "",
  277. CrossAnswer: "",
  278. userAnswerJudge:
  279. items.answer || items.isCross ? "[JUDGE##F##JUDGE]" : "",
  280. });
  281. });
  282. });
  283. this.$set(this.curQue, "Bookanswer", arr);
  284. } else {
  285. this.curQue.Bookanswer.forEach((item) => {
  286. item.content.forEach((item) => {
  287. if (item.userAnswerJudge == "[JUDGE##F##JUDGE]") {
  288. this.userError = true;
  289. return;
  290. }
  291. });
  292. });
  293. }
  294. },
  295. methods: {
  296. enterAnswer(i, j, type) {
  297. if (type == "input") {
  298. this.$forceUpdate();
  299. if (
  300. this.curQue.Bookanswer[i].content[j].answer ==
  301. this.curQue.tableData.body[i].content[j].answer
  302. ) {
  303. if (this.curQue.tableData.body[i].content[j].isCross) {
  304. if (
  305. this.curQue.Bookanswer[i].content[j].CrossAnswer ==
  306. this.curQue.tableData.body[i].content[j].CrossAnswer
  307. ) {
  308. this.curQue.Bookanswer[i].content[j].userAnswerJudge =
  309. "[JUDGE##T##JUDGE]";
  310. } else {
  311. this.curQue.Bookanswer[i].content[j].userAnswerJudge =
  312. "[JUDGE##F##JUDGE]";
  313. }
  314. } else {
  315. this.curQue.Bookanswer[i].content[j].userAnswerJudge =
  316. "[JUDGE##T##JUDGE]";
  317. }
  318. } else {
  319. if (this.curQue.tableData.body[i].content[j].answer) {
  320. this.curQue.Bookanswer[i].content[j].userAnswerJudge =
  321. "[JUDGE##F##JUDGE]";
  322. }
  323. }
  324. } else {
  325. if (
  326. this.curQue.Bookanswer[i].content[j].CrossAnswer ==
  327. this.curQue.tableData.body[i].content[j].CrossAnswer
  328. ) {
  329. if (this.curQue.tableData.body[i].content[j].answer) {
  330. if (
  331. this.curQue.Bookanswer[i].content[j].answer ==
  332. this.curQue.tableData.body[i].content[j].answer
  333. ) {
  334. this.curQue.Bookanswer[i].content[j].userAnswerJudge =
  335. "[JUDGE##T##JUDGE]";
  336. } else {
  337. this.curQue.Bookanswer[i].content[j].userAnswerJudge =
  338. "[JUDGE##F##JUDGE]";
  339. }
  340. } else {
  341. this.curQue.Bookanswer[i].content[j].userAnswerJudge =
  342. "[JUDGE##T##JUDGE]";
  343. }
  344. } else {
  345. this.curQue.Bookanswer[i].content[j].userAnswerJudge =
  346. "[JUDGE##F##JUDGE]";
  347. }
  348. }
  349. },
  350. // 控制首尾表格显隐
  351. tdHeaderIsNone(i, j) {
  352. let body = this.curQue.tableData.body;
  353. if (j !== 0 && j !== body[0].content.length - 1) return "table-cell";
  354. let col = 1;
  355. let colIndex = body[i].content.findIndex(({ colspan }, index) => {
  356. if (index > j) return false;
  357. let num = colspan === undefined ? 1 : Number(colspan);
  358. if (num > 1) {
  359. col = num;
  360. return false;
  361. }
  362. if (index === j && col > 1) return true;
  363. if (col > 0) col -= 1;
  364. return false;
  365. });
  366. let row = 1;
  367. let rowIndex = body.findIndex((item, index) => {
  368. let rowspan = item.content[j].rowspan;
  369. let num = rowspan === undefined ? 1 : Number(rowspan);
  370. if (num > 1) {
  371. row = num;
  372. return false;
  373. }
  374. if (index === i && row > 1) return true;
  375. if (row > 0) row -= 1;
  376. return false;
  377. });
  378. return rowIndex === -1 && colIndex === -1 ? "table-cell" : "none";
  379. },
  380. // rowspan colspan 控制td是否生成
  381. tdIsShow(i, j) {
  382. let body = this.curQue.tableData.body;
  383. if (j === 0) return true;
  384. let col = 1;
  385. let colIndex = body[i].content.findIndex(({ colspan }, index) => {
  386. if (index > j) return false;
  387. let num = colspan === undefined ? 1 : Number(colspan);
  388. if (num > 1) {
  389. col = num;
  390. return false;
  391. }
  392. if (index === j && col > 1) return true;
  393. if (col > 0) col -= 1;
  394. return false;
  395. });
  396. let row = 1;
  397. let rowIndex = body.findIndex((item, index) => {
  398. let rowspan = item.content[j].rowspan;
  399. let num = rowspan === undefined ? 1 : Number(rowspan);
  400. if (num > 1) {
  401. row = num;
  402. return false;
  403. }
  404. if (index === i && row > 1) return true;
  405. if (row > 0) row -= 1;
  406. return false;
  407. });
  408. return (
  409. colIndex === -1 && (rowIndex === -1 || j === body[0].content.length - 1)
  410. );
  411. },
  412. },
  413. };
  414. </script>
  415. <style lang="scss" scoped>
  416. .header-separate {
  417. width: 100%;
  418. margin-bottom: 16px;
  419. table {
  420. table-layout: fixed;
  421. font-size: 16px;
  422. border-collapse: separate;
  423. border-spacing: 6px 0;
  424. th {
  425. color: #4d4c51;
  426. font-weight: normal;
  427. background-color: #efeff9;
  428. padding: 4px 12px;
  429. border: 2px solid #afb4d1;
  430. .thead-content {
  431. display: flex;
  432. justify-content: center;
  433. .english {
  434. font-family: "robot";
  435. color: #000;
  436. }
  437. }
  438. }
  439. td {
  440. color: #474747;
  441. border-bottom: 1px solid transparent;
  442. padding: 4px 12px;
  443. min-height: 43px;
  444. height: 43px;
  445. .cell-wrap {
  446. display: flex;
  447. align-items: center;
  448. justify-content: space-between;
  449. .content {
  450. font-family: "FZJCGFKTK", "GB-PINYINOK-B", "robot";
  451. }
  452. }
  453. // 下划线
  454. &.underline {
  455. text-decoration: underline;
  456. }
  457. .sentence {
  458. display: flex;
  459. }
  460. // 前缀 + 拼音
  461. .pre-pinyin {
  462. display: flex;
  463. align-items: flex-end;
  464. .right-pinyin {
  465. margin-left: 4px;
  466. column-gap: 2px;
  467. text-align: center;
  468. display: grid;
  469. }
  470. }
  471. &:first-child {
  472. border-left: 2px solid transparent;
  473. border-bottom-width: 1px;
  474. border-image: linear-gradient(
  475. transparent 6px,
  476. #e7b576 6px,
  477. #e7b576 calc(100% - 6px),
  478. transparent calc(100% - 6px),
  479. transparent calc(100% - 2px),
  480. #cecece calc(100% - 2px)
  481. )
  482. 2;
  483. border-image-outset: 0 4px 0 0;
  484. &.col-center {
  485. text-align: center;
  486. }
  487. }
  488. &:not(:last-child) {
  489. position: relative;
  490. // 用 ::after 模拟中间边框
  491. &::after {
  492. content: "";
  493. position: absolute;
  494. top: 0;
  495. left: calc(100% + 2px);
  496. width: 2px;
  497. height: 100%;
  498. display: inline-block;
  499. background: repeating-linear-gradient(
  500. transparent,
  501. transparent 3px,
  502. #cecece 3px,
  503. #cecece 7px,
  504. transparent 7px
  505. );
  506. }
  507. }
  508. // 中间的底部边框用 ::before 模拟
  509. &:not(:first-child):not(:last-child)::before {
  510. content: "";
  511. position: absolute;
  512. top: 100%;
  513. left: 0;
  514. width: 100%;
  515. height: 1px;
  516. display: inline-block;
  517. background-color: #cecece;
  518. box-shadow: 2px 0 #cecece, -2px 0 #cecece;
  519. }
  520. &:last-child {
  521. border-right: 2px solid transparent;
  522. border-bottom-width: 1px;
  523. border-image: linear-gradient(
  524. transparent 6px,
  525. #e7b576 6px,
  526. #e7b576 calc(100% - 6px),
  527. transparent calc(100% - 6px),
  528. transparent calc(100% - 2px),
  529. #cecece calc(100% - 2px)
  530. )
  531. 2;
  532. border-image-outset: 0 0 0 4px;
  533. }
  534. }
  535. .pinyin {
  536. font-family: "GB-PINYINOK-B";
  537. }
  538. .chs {
  539. font-family: "FZJCGFKTK";
  540. }
  541. }
  542. }
  543. </style>
  544. <style lang="scss">
  545. .header-separate {
  546. .correct {
  547. .el-textarea.is-disabled .el-textarea__inner {
  548. color: #2ca767 !important;
  549. }
  550. .cross-tick {
  551. border-color: #2ca767 !important;
  552. .el-icon-check:before {
  553. color: #2ca767 !important;
  554. }
  555. .el-icon-close:before {
  556. color: #2ca767 !important;
  557. }
  558. }
  559. }
  560. .error {
  561. .el-textarea.is-disabled .el-textarea__inner {
  562. color: #ed342d !important;
  563. }
  564. .cross-tick {
  565. border-color: #ed342d !important;
  566. .el-icon-check:before {
  567. color: #ed342d !important;
  568. }
  569. .el-icon-close:before {
  570. color: #ed342d !important;
  571. }
  572. }
  573. }
  574. }
  575. </style>