ConfigurableTable.vue 17 KB

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