WordCardPreview.vue 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. <!-- eslint-disable vue/no-v-html -->
  2. <template>
  3. <div class="word-card-preview">
  4. <div class="stem">
  5. <span class="question-number">{{ data.property.question_number }}.</span>
  6. <span v-html="sanitizeHTML(data.stem)"></span>
  7. </div>
  8. <div
  9. v-if="isEnable(data.property.is_enable_description)"
  10. class="description rich-text"
  11. v-html="sanitizeHTML(data.description)"
  12. ></div>
  13. <!-- 笔画学习 -->
  14. <div :class="['words-box']">
  15. <el-image
  16. v-if="
  17. option_list[active_index] &&
  18. option_list[active_index].picture_file_id &&
  19. pic_list[option_list[active_index].picture_file_id]
  20. "
  21. :src="pic_list[option_list[active_index].picture_file_id]"
  22. fit="cover"
  23. />
  24. <div class="words-right">
  25. <template v-for="(item, index) in option_list">
  26. <div v-if="index === active_index" :key="index" class="strock-box">
  27. <div class="pinyin-box" v-if="item.audio_file_id || item.pinyin">
  28. <AudioPlay v-if="item.audio_file_id" :file-id="item.audio_file_id" theme-color="white" />
  29. <span class="pinyin">{{ item.pinyin }}</span>
  30. </div>
  31. <div v-if="item.hz_strokes_list && item.hz_strokes_list.length > 0" class="strock-left">
  32. <template v-for="(items, indexs) in item.hz_strokes_list">
  33. <Strockplayredline
  34. v-if="items"
  35. :key="indexs"
  36. :play-storkes="true"
  37. :book-text="items.hz"
  38. :target-div="'pre' + items.hz + indexs + active_index"
  39. :book-strokes="items.strokes"
  40. :class="['strock-chinese', indexs !== item.hz_strokes_list.length - 1 ? 'border-right-none' : '']"
  41. />
  42. </template>
  43. </div>
  44. </div>
  45. </template>
  46. <el-divider />
  47. <div v-if="option_list[active_index]" class="content-box">
  48. <span v-if="option_list[active_index].definition_preview.length > 0" class="tips">释义:</span>
  49. <p v-for="(itemd, indexd) in option_list[active_index].definition_preview" :key="indexd" class="definition">
  50. {{ itemd }}
  51. </p>
  52. <span v-if="option_list[active_index].collocation" class="tips">搭配:</span>
  53. <p v-if="option_list[active_index].collocation" class="definition">
  54. {{ option_list[active_index].collocation }}
  55. </p>
  56. <span v-if="option_list[active_index].example_sentence.length > 0" class="tips">例句:</span>
  57. <template v-for="(iteme, indexe) in option_list[active_index].example_sentence">
  58. <p v-if="iteme.trim()" :key="indexe + iteme.trim()" class="example-sentence">
  59. <span>{{ computeOptionMethods[data.option_number_show_mode](indexe) }} </span>
  60. <span>{{ iteme }}</span>
  61. </p>
  62. </template>
  63. </div>
  64. <el-divider />
  65. <div v-if="answer.answer_list[active_index]" class="sound-box">
  66. <SoundRecordPreview
  67. :wav-blob.sync="answer.answer_list[active_index].audio_file_id"
  68. :type="'small'"
  69. :disabled="disabled"
  70. />
  71. </div>
  72. </div>
  73. <div v-if="option_list.length > 1" :class="['words-item']">
  74. <label
  75. v-for="(item, index) in option_list"
  76. :key="index"
  77. :class="[active_index === index ? 'active' : '']"
  78. @click="active_index = index"
  79. >{{ item.content }}</label
  80. >
  81. </div>
  82. </div>
  83. </div>
  84. </template>
  85. <script>
  86. import { computeOptionMethods } from '@/views/exercise_questions/data/common';
  87. import PreviewMixin from './components/PreviewMixin';
  88. import { GetFileStoreInfo } from '@/api/app';
  89. import SoundRecordPreview from './components/common/SoundRecordPreview.vue';
  90. import Strockplayredline from './components/common/Strockplayredline.vue';
  91. export default {
  92. name: 'WordCardPreview',
  93. components: { SoundRecordPreview, Strockplayredline },
  94. mixins: [PreviewMixin],
  95. data() {
  96. return {
  97. computeOptionMethods,
  98. hanzi_color: '#404040', // 描红汉字底色
  99. pic_list: {},
  100. active_index: 0,
  101. option_list: [],
  102. };
  103. },
  104. watch: {
  105. data: {
  106. handler(val) {
  107. if (!val || this.data.type !== 'word_card') return;
  108. this.handleData();
  109. },
  110. deep: true,
  111. immediate: true,
  112. },
  113. },
  114. created() {
  115. // this.handleData();
  116. },
  117. mounted() {},
  118. methods: {
  119. // 初始化数据
  120. handleData() {
  121. this.pic_list = {};
  122. this.data.file_id_list.forEach((item) => {
  123. GetFileStoreInfo({ file_id: item }).then(({ file_id, file_url }) => {
  124. this.$set(this.pic_list, file_id, file_url);
  125. });
  126. });
  127. let option_list = JSON.parse(JSON.stringify(this.data.option_list));
  128. option_list.forEach((item) => {
  129. let obj = {
  130. mark: item.mark,
  131. audio_file_id: '',
  132. };
  133. item.definition_preview = item.definition.split('\n');
  134. if (!this.isJudgingRightWrong) {
  135. this.answer.answer_list.push(obj);
  136. }
  137. });
  138. this.option_list = option_list;
  139. },
  140. },
  141. };
  142. </script>
  143. <style lang="scss" scoped>
  144. @use '@/styles/mixin.scss' as *;
  145. .word-card-preview {
  146. @include preview;
  147. .words-box {
  148. display: flex;
  149. column-gap: 24px;
  150. .el-image {
  151. flex-shrink: 0;
  152. width: 346px;
  153. height: 346px;
  154. }
  155. .words-right {
  156. flex: 1;
  157. }
  158. .words-item {
  159. width: 120px;
  160. label {
  161. display: block;
  162. padding: 8px 16px;
  163. margin-bottom: 4px;
  164. font-size: 18px;
  165. font-weight: 400;
  166. line-height: 26px;
  167. color: rgba(0, 0, 0, 30%);
  168. cursor: pointer;
  169. border-radius: 20px;
  170. &.active {
  171. color: #fff;
  172. background: rgba(48, 110, 255, 100%);
  173. }
  174. }
  175. }
  176. .pinyin {
  177. font-family: 'League';
  178. font-size: 16px;
  179. font-weight: 500;
  180. color: #fff;
  181. }
  182. .strock-chinese {
  183. width: 96px;
  184. height: 96px;
  185. border: 1px solid #e81b1b;
  186. :deep .strock-play-box {
  187. width: 16px;
  188. height: 16px;
  189. }
  190. }
  191. .border-right-none {
  192. border-right: none;
  193. }
  194. }
  195. .strock-left {
  196. display: flex;
  197. }
  198. .pinyin-box {
  199. display: flex;
  200. gap: 4px;
  201. align-items: center;
  202. width: max-content;
  203. padding: 4px 8px;
  204. margin-bottom: 10px;
  205. background: rgba(47, 111, 236, 100%);
  206. border-radius: 40px;
  207. :deep .audio-play {
  208. width: auto;
  209. height: 24px;
  210. background: none;
  211. }
  212. }
  213. .definition {
  214. margin: 0 0 8px;
  215. font-size: 16px;
  216. line-height: 24px;
  217. color: #000;
  218. }
  219. .tips {
  220. display: block;
  221. margin-bottom: 8px;
  222. font-size: 16px;
  223. font-weight: 400;
  224. line-height: 24px;
  225. color: rgba(0, 0, 0, 40%);
  226. }
  227. .example-sentence {
  228. margin: 0;
  229. font-size: 16px;
  230. line-height: 24px;
  231. color: #000;
  232. }
  233. .sound-box {
  234. width: max-content;
  235. padding: 8px;
  236. background: $content-color;
  237. border-radius: 40px;
  238. }
  239. .el-divider {
  240. margin: 16px 0;
  241. }
  242. }
  243. </style>