CharacterStructurePreview.vue 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656
  1. <!-- eslint-disable vue/no-v-html -->
  2. <template>
  3. <div class="structure-preview" :style="[getAreaStyle(), getComponentStyle()]">
  4. <SerialNumberPosition v-if="isEnable(data.property.sn_display_mode)" :property="data.property" />
  5. <div class="main">
  6. <div
  7. class="option"
  8. :style="{
  9. background: data.unified_attrib && data.unified_attrib.assist_color ? data.unified_attrib.assist_color : '',
  10. }"
  11. >
  12. <draggable
  13. v-model="SortArr"
  14. animation="300"
  15. group="site"
  16. class="content-box"
  17. :disabled="disabled"
  18. @start="onStart($event)"
  19. @end="onEnd($event)"
  20. @choose="choose($event)"
  21. >
  22. <transition-group>
  23. <div
  24. v-for="(item, i) in data.structure_select_list"
  25. :id="item.id"
  26. :key="'op' + i"
  27. class="option_one"
  28. :class="['option_one-' + data.property.frame_size]"
  29. :style="{
  30. background:
  31. data.unified_attrib && data.unified_attrib.topic_color ? data.unified_attrib.topic_color : '',
  32. }"
  33. >
  34. <img
  35. :src="item.type === 'local' ? require('@/assets/structure/' + item.value) : item.value"
  36. class="small-img"
  37. />
  38. </div>
  39. </transition-group>
  40. </draggable>
  41. </div>
  42. <div class="one-box">
  43. <div
  44. v-for="(items, row) in data.option_list"
  45. :key="'row' + row"
  46. class="one"
  47. :class="[!items.pinyin ? 'one_nopy' : '']"
  48. :style="{ marginRight: (row + 1) % 3 == 0 ? '' : '16px' }"
  49. >
  50. <div
  51. class="number"
  52. :style="{
  53. background: data.unified_attrib && data.unified_attrib.topic_color ? data.unified_attrib.topic_color : '',
  54. }"
  55. >
  56. {{ row + 1 }}
  57. </div>
  58. <div class="hzpinyin">
  59. <div
  60. v-if="isEnable(data.property.view_pinyin)"
  61. class="pinyin"
  62. :style="{
  63. fontSize: data.unified_attrib && data.unified_attrib.pinyin_size ? data.unified_attrib.pinyin_size : '',
  64. }"
  65. >
  66. {{ items.pinyin }}
  67. </div>
  68. <template v-if="items.hz_info.length > 0">
  69. <!-- @click="writeWord(conItem, items.pinyin)" -->
  70. <div
  71. class="strockplay-newWord"
  72. :class="['frame-size-' + data.property.frame_size]"
  73. :style="{
  74. borderColor:
  75. data.unified_attrib && data.unified_attrib.topic_color
  76. ? data.unified_attrib.topic_color
  77. : '#346CDA',
  78. }"
  79. >
  80. <Strockplay
  81. class-name="adult-strockplay"
  82. :Book_text="items.hz_info[0].con"
  83. :play-storkes="true"
  84. :stroke-play-color="
  85. data.unified_attrib && data.unified_attrib.topic_color ? data.unified_attrib.topic_color : '#346CDA'
  86. "
  87. :stroke-color="'#000000'"
  88. :paly-width="'18px'"
  89. :BoxbgType="'0'"
  90. :cur-item="items.hz_info[0].hzDetail.hz_json"
  91. :target-div="'writeTops-item-' + '-' + items.hz_info[0].con + row"
  92. class="writeTop-item"
  93. :style="{
  94. borderColor:
  95. data.unified_attrib && data.unified_attrib.topic_color
  96. ? data.unified_attrib.topic_color
  97. : '#346CDA',
  98. }"
  99. />
  100. </div>
  101. </template>
  102. </div>
  103. <div class="image">
  104. <img src="@/assets/drag-arrows.png" alt="" />
  105. </div>
  106. <div class="answer" :class="['answer-' + data.property.frame_size]">
  107. <!-- :disabled="TaskModel == 'ANSWER' ? true : items.is_example ? true : false" -->
  108. <draggable
  109. v-model="answer.answer_list[row].answer_list"
  110. animation="300"
  111. group="site"
  112. :disabled="items.is_example"
  113. class="content-box content-boxs"
  114. :class="['content-boxs-' + data.property.frame_size]"
  115. :move="onMove"
  116. >
  117. <transition-group>
  118. <div
  119. v-for="(answer, i) in answer.answer_list[row].answer_list"
  120. :key="'op' + i"
  121. class="option_one"
  122. :class="[
  123. items.is_example ? 'option_one_example' : classNameJudge(items, row),
  124. 'option_one-' + data.property.frame_size,
  125. ]"
  126. :index="'form' + i"
  127. :style="{
  128. borderColor:
  129. data.unified_attrib && data.unified_attrib.topic_color ? data.unified_attrib.topic_color : '',
  130. }"
  131. >
  132. <img
  133. v-if="answer.img_url || answer.value"
  134. :src="
  135. answer.img_url
  136. ? answer.img_url
  137. : answer.type === 'local'
  138. ? require('@/assets/structure/' + answer.value)
  139. : answer.value
  140. "
  141. alt=""
  142. />
  143. </div>
  144. <div
  145. v-if="answer.answer_list[row].answer_list.length == 0"
  146. :key="row"
  147. class="option_one"
  148. :class="[
  149. isJudgingRightWrong && items.answer ? 'wrong' : '',
  150. 'option_one-' + data.property.frame_size,
  151. ]"
  152. :style="{
  153. borderColor:
  154. data.unified_attrib && data.unified_attrib.topic_color ? data.unified_attrib.topic_color : '',
  155. }"
  156. ></div>
  157. </transition-group>
  158. </draggable>
  159. </div>
  160. </div>
  161. </div>
  162. <PreviewOperation @showAnswerAnalysis="showAnswerAnalysis" @judgeCorrect="judgeCorrect" @retry="retry" />
  163. <AnswerCorrect
  164. :answer-correct="data?.answer_correct"
  165. :visible.sync="visibleAnswerCorrect"
  166. :is-check-correct="isCheckCorrect"
  167. @closeAnswerCorrect="closeAnswerCorrect"
  168. />
  169. <AnswerAnalysis
  170. :visible.sync="visibleAnswerAnalysis"
  171. :answer-list="data.answer_list"
  172. :analysis-list="data.analysis_list"
  173. @closeAnswerAnalysis="closeAnswerAnalysis"
  174. >
  175. <div slot="right-answer">
  176. <div class="one-box">
  177. <div
  178. v-for="(items, row) in data.option_list"
  179. :key="'row' + row"
  180. class="one"
  181. :class="[!items.pinyin ? 'one_nopy' : '']"
  182. :style="{ marginRight: (row + 1) % 3 == 0 ? '' : '16px' }"
  183. >
  184. <div
  185. class="number"
  186. :style="{
  187. background:
  188. data.unified_attrib && data.unified_attrib.topic_color
  189. ? data.unified_attrib.topic_color
  190. : '#346CDA',
  191. }"
  192. >
  193. {{ row + 1 }}
  194. </div>
  195. <div class="hzpinyin">
  196. <div v-if="isEnable(data.property.view_pinyin)" class="pinyin">
  197. {{ items.pinyin }}
  198. </div>
  199. <template v-if="items.hz_info.length > 0">
  200. <div
  201. class="strockplay-newWord"
  202. :class="['frame-size-' + data.property.frame_size]"
  203. :style="{
  204. borderColor:
  205. data.unified_attrib && data.unified_attrib.topic_color
  206. ? data.unified_attrib.topic_color
  207. : '#346CDA',
  208. }"
  209. >
  210. <Strockplay
  211. class-name="adult-strockplay"
  212. :Book_text="items.hz_info[0].con"
  213. :play-storkes="true"
  214. :stroke-play-color="
  215. data.unified_attrib && data.unified_attrib.topic_color
  216. ? data.unified_attrib.topic_color
  217. : '#346CDA'
  218. "
  219. :stroke-color="'#000000'"
  220. :paly-width="'18px'"
  221. :BoxbgType="'0'"
  222. :cur-item="items.hz_info[0].hzDetail.hz_json"
  223. :target-div="'writeTops-item-right' + '-' + items.hz_info[0].con"
  224. class="writeTop-item"
  225. :style="{
  226. borderColor:
  227. data.unified_attrib && data.unified_attrib.topic_color
  228. ? data.unified_attrib.topic_color
  229. : '#346CDA',
  230. }"
  231. />
  232. </div>
  233. </template>
  234. </div>
  235. <div class="image">
  236. <img src="@/assets/drag-arrows.png" alt="" />
  237. </div>
  238. <div class="answer">
  239. <div
  240. class="option_one"
  241. :class="[items.is_example ? 'option_one_example' : '', 'option_one-' + data.property.frame_size]"
  242. :style="{
  243. borderColor:
  244. data.unified_attrib && data.unified_attrib.topic_color
  245. ? data.unified_attrib.topic_color
  246. : '#346CDA',
  247. }"
  248. >
  249. <img
  250. v-if="items.answer"
  251. :src="
  252. items.answer.length > 3
  253. ? data.file_list.find((p) => p.file_id === items.answer).file_url
  254. : require('@/assets/structure/structure-' + items.answer + '.png')
  255. "
  256. alt=""
  257. />
  258. </div>
  259. </div>
  260. </div>
  261. </div>
  262. </div>
  263. </AnswerAnalysis>
  264. </div>
  265. </div>
  266. </template>
  267. <script>
  268. import { getCharacterStructureData } from '@/views/book/courseware/data/characterStructure';
  269. import PreviewMixin from '../common/PreviewMixin';
  270. import draggable from 'vuedraggable';
  271. import Strockplay from '../new_word/components/Strockplay.vue';
  272. import { getRandomNumber } from '@/utils';
  273. export default {
  274. name: 'CharacterStructurePreview',
  275. components: { draggable, Strockplay },
  276. mixins: [PreviewMixin],
  277. inject: {
  278. bookStatus: {
  279. default: 'online',
  280. },
  281. },
  282. data() {
  283. return {
  284. data: getCharacterStructureData(),
  285. dragData: [],
  286. SortArr: [],
  287. single: [],
  288. drag: false,
  289. currentId: null,
  290. isPraShow: false,
  291. curData: null,
  292. userErrorList: [],
  293. correctArr: [],
  294. };
  295. },
  296. // 计算属性 类似于data概念
  297. computed: {},
  298. watch: {
  299. 'data.option_list': {
  300. handler(val) {
  301. if (val) {
  302. this.handleData();
  303. }
  304. },
  305. deep: true,
  306. immediate: true,
  307. },
  308. },
  309. created() {
  310. // if (this.bookStatus === 'offline') {
  311. // this.$message.warning('播放笔画动画功能只有在线教材可以支持,请使用在线教材');
  312. // }
  313. },
  314. methods: {
  315. onMove(e) {
  316. return false;
  317. },
  318. choose(e) {
  319. let index = e.item.outerHTML.indexOf('id');
  320. let formIndex = e.item.outerHTML[index + 4];
  321. let formIndex2 = e.item.outerHTML[index + 5];
  322. if (!isNaN(Number(formIndex))) {
  323. this.currentId = Number(formIndex);
  324. }
  325. if (!isNaN(Number(formIndex2))) {
  326. this.currentId = (String(this.currentId) + formIndex2) * 1;
  327. }
  328. },
  329. // 开始拖拽事件
  330. onStart() {
  331. this.drag = true;
  332. this.dragData = JSON.parse(JSON.stringify(this.SortArr));
  333. },
  334. // 拖拽结束事件
  335. onEnd(e) {
  336. this.drag = false;
  337. let formIndex = Number(e.to.firstChild.attributes[1].value[4]);
  338. if (`${formIndex}` !== 'NaN') {
  339. if (this.answer.answer_list[formIndex].answer_list.length > 1) {
  340. let arr = [];
  341. this.data.option_list.forEach((item) => {
  342. if (item.id === this.currentId) {
  343. arr.push(item);
  344. }
  345. });
  346. this.answer.answer_list[formIndex].answer_list = JSON.parse(JSON.stringify(arr));
  347. }
  348. }
  349. this.SortArr = JSON.parse(JSON.stringify(this.dragData));
  350. },
  351. handleData() {
  352. this.single = [];
  353. this.data.structure_select_list.forEach((item) => {
  354. let items = this.data.file_list.find((p) => p.file_id === item.file_id);
  355. if (items) {
  356. item.value = items.file_url;
  357. }
  358. });
  359. this.data.option_list.forEach((items) => {
  360. if (items.is_example) {
  361. let obj = {
  362. id: items.answer,
  363. img_list: [],
  364. img_url:
  365. items.answer.length > 3
  366. ? this.data.structure_select_list.find((p) => p.file_id === items.answer)
  367. ? this.data.structure_select_list.find((p) => p.file_id === items.answer).value
  368. : ''
  369. : require(`@/assets/structure/structure-${items.answer}.png`),
  370. userAnswerJudge: 'example',
  371. };
  372. this.single.push({
  373. answer_list: [obj],
  374. });
  375. } else {
  376. this.single.push({
  377. answer_list: [],
  378. });
  379. }
  380. });
  381. this.data.structure_select_list.forEach((item, i) => {
  382. item.id = i;
  383. this.SortArr.push(item);
  384. });
  385. if (!this.isJudgingRightWrong) {
  386. this.$set(this.answer, 'answer_list', this.single);
  387. }
  388. },
  389. classNameJudge(item, index) {
  390. let classname = '';
  391. if (!this.isJudgingRightWrong && !this.isShowRightAnswer) {
  392. return '';
  393. }
  394. if (item.answer && this.isJudgingRightWrong) {
  395. if (item.answer === this.answer.answer_list[index].answer_list[0].file_id) {
  396. classname = 'right';
  397. } else {
  398. classname = 'wrong';
  399. }
  400. }
  401. return classname;
  402. },
  403. // 重做
  404. retry() {
  405. this.isJudgingRightWrong = false;
  406. this.isShowRightAnswer = false;
  407. this.handleData([]);
  408. },
  409. /**
  410. * 获取无文本内容的数据结构,用于保存为个人模板时的样式模板
  411. */
  412. getNoTextContentData() {
  413. let noTextContentData = JSON.parse(JSON.stringify(this.data));
  414. const resetFieldMap = {
  415. analysis_list: [],
  416. answer_list: [],
  417. structure_select_id: [], // 存放已选选项id
  418. structure_select_list: [],
  419. image_list: [],
  420. image_id_list: [],
  421. file_list: [],
  422. file_id_list: [],
  423. };
  424. Object.assign(noTextContentData, resetFieldMap);
  425. noTextContentData.option_list.forEach((item) => {
  426. item.content = '';
  427. item.pinyin = '';
  428. item.mark = getRandomNumber();
  429. item.hz_info = [];
  430. item.is_example = false;
  431. item.answer = '';
  432. });
  433. if (noTextContentData.answer) {
  434. noTextContentData.answer.answer_list = [];
  435. noTextContentData.answer.reference_answer = '';
  436. }
  437. return noTextContentData;
  438. },
  439. },
  440. };
  441. </script>
  442. <style lang="scss" scoped>
  443. @use '@/styles/mixin.scss' as *;
  444. .structure-preview {
  445. .content-box {
  446. span {
  447. display: flex;
  448. flex-wrap: wrap;
  449. }
  450. }
  451. .content-boxs {
  452. height: 80px;
  453. overflow: hidden;
  454. &-middle {
  455. height: 70px;
  456. }
  457. &-small {
  458. height: 60px;
  459. }
  460. }
  461. .option {
  462. display: flex;
  463. flex-wrap: wrap;
  464. padding: 10px 15px;
  465. background: #deebff;
  466. border-radius: 4px;
  467. .option_one {
  468. display: flex;
  469. align-items: center;
  470. justify-content: center;
  471. width: 72px;
  472. height: 72px;
  473. margin: 10px 15px;
  474. overflow: hidden;
  475. cursor: pointer;
  476. background: #9dcaff;
  477. border-radius: 4px;
  478. &-middle {
  479. width: 62px;
  480. height: 62px;
  481. }
  482. &-small {
  483. width: 52px;
  484. height: 52px;
  485. }
  486. img {
  487. max-width: 100%;
  488. max-height: 100%;
  489. // position: relative;
  490. // top: 2px;
  491. opacity: 0.8;
  492. }
  493. }
  494. }
  495. .strockplay-newWord {
  496. position: relative;
  497. box-sizing: border-box;
  498. width: 80px;
  499. height: 80px;
  500. // padding: 5px;
  501. overflow: hidden;
  502. // background: #fff;
  503. background-size: cover;
  504. border: 2px solid #346cda;
  505. border-radius: 4px;
  506. }
  507. .frame-size-middle {
  508. width: 70px !important;
  509. height: 70px !important;
  510. }
  511. .frame-size-small {
  512. width: 60px !important;
  513. height: 60px !important;
  514. }
  515. .one {
  516. display: flex;
  517. align-items: center;
  518. margin: 35px 0 32px;
  519. &.one_nopy {
  520. margin: 16px 0;
  521. }
  522. .number {
  523. display: block;
  524. width: 24px;
  525. height: 24px;
  526. margin-right: 16px;
  527. font-family: 'arial';
  528. font-size: 14px;
  529. font-weight: bold;
  530. line-height: 24px;
  531. color: #fff;
  532. text-align: center;
  533. background: #346cda;
  534. border-radius: 100%;
  535. }
  536. .hzpinyin {
  537. position: relative;
  538. .pinyin {
  539. position: absolute;
  540. top: -28px;
  541. width: 100%;
  542. font-family: 'League';
  543. font-size: 16px;
  544. font-weight: 400;
  545. color: rgba($color: #000, $alpha: 50%);
  546. text-align: center;
  547. }
  548. }
  549. .image {
  550. img {
  551. width: 15px;
  552. margin: 0 9px;
  553. }
  554. }
  555. .answer {
  556. width: 80px;
  557. height: 80px;
  558. &-middle {
  559. width: 70px;
  560. height: 70px;
  561. }
  562. &-small {
  563. width: 60px;
  564. height: 60px;
  565. }
  566. .option_one {
  567. display: flex;
  568. align-items: center;
  569. justify-content: center;
  570. width: 80px;
  571. height: 80px;
  572. overflow: hidden;
  573. border: 2px solid #346cda;
  574. border-radius: 4px;
  575. &-middle {
  576. width: 70px;
  577. height: 70px;
  578. }
  579. &-small {
  580. width: 60px;
  581. height: 60px;
  582. }
  583. &.right {
  584. background: #e9f7f2 !important;
  585. border-color: $right-color !important;
  586. }
  587. &.wrong {
  588. border-color: $error-color !important;
  589. }
  590. img {
  591. max-width: 100%;
  592. max-height: 100%;
  593. // position: relative;
  594. // left: -2px;
  595. opacity: 0.8;
  596. }
  597. }
  598. }
  599. .answer:hover {
  600. // @include background_color("theme_color");
  601. }
  602. }
  603. .one-box {
  604. display: flex;
  605. flex-flow: wrap;
  606. column-gap: 16px;
  607. padding: 20px 0;
  608. }
  609. }
  610. </style>