index.vue 17 KB

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