ChinesePreview.vue 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. <!-- eslint-disable vue/no-v-html -->
  2. <template>
  3. <div class="chinese-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 v-if="isEnable(data.property.is_enable_description)" class="description">{{ data.description }}</div>
  9. <!-- 笔画学习 -->
  10. <div :class="['words-box', 'words-box-' + data.property.learn_type]">
  11. <div v-for="(item, index) in data.option_list" :key="index" :class="['words-item']">
  12. <template v-if="item.content.trim()">
  13. <template v-if="data.property.learn_type === 'paint'">
  14. <!-- 描红 -->
  15. <Strockplayredline
  16. :play-storkes="true"
  17. :book-text="item.content"
  18. :target-div="'pre' + item.content + index"
  19. :book-strokes="item.strokes"
  20. :class="['strock-chinese', 'border-right-none']"
  21. />
  22. <Strockred
  23. v-for="itemI in 5"
  24. :key="itemI + data.property.learn_type"
  25. :book-text="item.content"
  26. :hanzi-color="hanzi_color"
  27. :reset="true"
  28. :target-div="'write-praT' + item.content + itemI + Math.random().toString(36).substring(2, 10)"
  29. :book-strokes="item.strokes"
  30. :class="['strock-chinese', itemI !== 5 ? 'border-right-none' : '']"
  31. />
  32. <div
  33. v-for="(items, indexs) in item.imgArr"
  34. :key="indexs"
  35. :class="['strockplay-newWord border-left-none']"
  36. @click="freeWrite(items, index, indexs)"
  37. >
  38. <SvgIcon icon-class="hanzi-writer-bg" class="character-target-bg" />
  39. <img
  40. v-if="!play_status && items && items.strokes_image_url"
  41. class="hanzi-writer-img"
  42. :src="items.strokes_image_url"
  43. alt=""
  44. />
  45. </div>
  46. </template>
  47. <template v-else-if="data.property.learn_type === 'write'">
  48. <!-- 书写 -->
  49. <Strockplayredline
  50. :play-storkes="true"
  51. :book-text="item.content"
  52. :target-div="'pre' + item.content + index"
  53. :book-strokes="item.strokes"
  54. :class="['strock-chinese']"
  55. />
  56. <div v-for="(items, indexs) in item.imgArr" :key="indexs" class="con-box">
  57. <div :class="['strockplay-newWord border-left-none']" @click="freeWrite(items, index, indexs)">
  58. <SvgIcon icon-class="hanzi-writer-bg" class="character-target-bg" />
  59. <img
  60. v-if="!play_status && items && items.strokes_image_url"
  61. class="hanzi-writer-img"
  62. :src="items.strokes_image_url"
  63. alt=""
  64. />
  65. </div>
  66. </div>
  67. </template>
  68. <template v-else>
  69. <div class="words-info">
  70. <AudioPlay :file-id="item.audio_file_id" theme-color="gray" />
  71. <span class="pinyin">{{ item.pinyin }}</span>
  72. </div>
  73. <Strockplayredline
  74. :play-storkes="true"
  75. :book-text="item.content"
  76. :target-div="'pre' + item.content + index"
  77. :book-strokes="item.strokes"
  78. class="strock-chinese"
  79. />
  80. </template>
  81. </template>
  82. </div>
  83. </div>
  84. <div v-if="if_free_show" class="practiceBox practice-box-strock">
  85. <FreewriteLettle
  86. ref="freePaint"
  87. :current-tree-i-d="'1234'"
  88. :current-hz="current_hz"
  89. :curren-hz-data="current_hz_data"
  90. :row-index="active_index"
  91. :col-index="active_col_index"
  92. @closeIfFreeShow="closeIfFreeShow"
  93. @changePraShow="changePraShow"
  94. @changeCurQue="changeCurQue"
  95. @deleteWriteRecord="deleteWriteRecord"
  96. />
  97. </div>
  98. </div>
  99. </template>
  100. <script>
  101. import PreviewMixin from './components/PreviewMixin';
  102. import Strockplayredline from './components/common/Strockplayredline.vue';
  103. import Strockred from './components/common/Strockred.vue';
  104. import FreewriteLettle from './components/common/FreewriteLettle.vue';
  105. import { GetStaticResources } from '@/api/exercise';
  106. export default {
  107. name: 'ChinesePreview',
  108. components: {
  109. Strockplayredline,
  110. Strockred,
  111. FreewriteLettle,
  112. },
  113. mixins: [PreviewMixin],
  114. data() {
  115. return {
  116. hanzi_color: '#404040', // 描红汉字底色
  117. writer_number: 19, // 书写个数
  118. answer_list: {
  119. write_model: {},
  120. }, // 用户答题数据
  121. if_free_show: false,
  122. free_img: [],
  123. active_index: null,
  124. active_col_index: null,
  125. current_hz: '', // 当前汉字
  126. current_hz_data: null, // 当前汉字数据
  127. play_status: false, // 播放状态
  128. };
  129. },
  130. watch: {
  131. 'data.property.learn_type': {
  132. handler(val, oldVal) {
  133. if (val !== oldVal) {
  134. this.handleData();
  135. }
  136. },
  137. deep: true,
  138. },
  139. },
  140. created() {
  141. this.handleData();
  142. },
  143. methods: {
  144. // 初始化数据
  145. handleData() {
  146. if (document.getElementsByClassName('preview-content') && document.getElementsByClassName('preview-content')[0]) {
  147. this.writer_number =
  148. Math.floor((document.getElementsByClassName('preview-content')[0].clientWidth - 128) / 64) - 1;
  149. }
  150. if (this.data.property.learn_type === 'paint') {
  151. this.writer_number -= 5;
  152. }
  153. this.data.option_list.forEach((item) => {
  154. if (item.content.trim()) {
  155. let arr = [];
  156. let MethodName = 'hz_resource_manager-GetHZStrokesContent';
  157. let data = {
  158. hz: item.content.trim(),
  159. };
  160. GetStaticResources(MethodName, data).then((res) => {
  161. this.$set(item, 'strokes', res);
  162. });
  163. for (let i = 0; i < this.writer_number; i++) {
  164. arr.push(null);
  165. }
  166. item.imgArr = arr;
  167. this.answer_list.write_model[item.content.trim()] = arr;
  168. }
  169. });
  170. },
  171. changePraShow() {
  172. this.if_free_show = false;
  173. },
  174. closeIfFreeShow(data, rowIndex, colIndex) {
  175. this.data.option_list[rowIndex].imgArr[colIndex] = data;
  176. this.if_free_show = false;
  177. this.freeWrite(data, rowIndex, colIndex);
  178. this.$forceUpdate();
  179. },
  180. freeWrite(imgUrl, index, indexs) {
  181. this.if_free_show = true;
  182. this.active_index = index;
  183. this.active_col_index = indexs;
  184. this.current_hz = this.data.option_list[index].content;
  185. this.current_hz_data = imgUrl;
  186. },
  187. // 删除记录
  188. deleteWriteRecord(rowIndex, colIndex, current_hz) {
  189. this.$set(this.data.option_list[rowIndex].imgArr, colIndex, {});
  190. let answer = {
  191. hz: current_hz,
  192. strokes_content: '',
  193. strokes_image_url: '',
  194. };
  195. this.changeCurQue(answer, colIndex);
  196. this.current_hz_data = null;
  197. this.$forceUpdate();
  198. },
  199. changeCurQue(answer, colIndex) {
  200. if (answer) {
  201. let write_model = this.answer_list.write_model;
  202. let hz = answer.hz;
  203. write_model[hz][colIndex] = answer;
  204. }
  205. },
  206. },
  207. };
  208. </script>
  209. <style lang="scss" scoped>
  210. @use '@/styles/mixin.scss' as *;
  211. .chinese-preview {
  212. @include preview;
  213. .words-box {
  214. .words-item {
  215. display: flex;
  216. min-width: 64px;
  217. margin-bottom: 24px;
  218. }
  219. .audio-wrapper {
  220. height: 16px;
  221. :deep .audio-play {
  222. width: 16px;
  223. height: 16px;
  224. color: #000;
  225. background-color: initial;
  226. }
  227. :deep .audio-play.not-url {
  228. color: #a1a1a1;
  229. }
  230. :deep .voice-play {
  231. width: 16px;
  232. height: 16px;
  233. }
  234. }
  235. .words-info {
  236. display: flex;
  237. column-gap: 4px;
  238. align-items: center;
  239. justify-content: center;
  240. margin-bottom: 9px;
  241. .pinyin {
  242. font-family: 'League';
  243. font-size: 12px;
  244. font-weight: 500;
  245. color: #000;
  246. }
  247. }
  248. .strock-chinese {
  249. border: 1px solid #e81b1b;
  250. }
  251. .strockplay-newWord {
  252. position: relative;
  253. box-sizing: border-box;
  254. flex-shrink: 0;
  255. width: 64px;
  256. height: 64px;
  257. border: 1px solid #e81b1b;
  258. .character-target-bg,
  259. .hanzi-writer-img {
  260. position: absolute;
  261. top: 0;
  262. left: 0;
  263. width: 100%;
  264. height: 100%;
  265. color: #dedede;
  266. }
  267. .hanzi-writer-img {
  268. z-index: 1;
  269. }
  270. }
  271. .border-left-none {
  272. border-left: none;
  273. }
  274. .border-right-none {
  275. border-right: none;
  276. }
  277. &-learn {
  278. display: flex;
  279. column-gap: 24px;
  280. .words-item {
  281. display: block;
  282. }
  283. }
  284. }
  285. .practiceBox {
  286. position: fixed;
  287. top: 0;
  288. left: 0;
  289. z-index: 101;
  290. box-sizing: border-box;
  291. width: 100%;
  292. height: 100vh;
  293. overflow: hidden;
  294. overflow-y: auto;
  295. background: rgba(0, 0, 0, 19%);
  296. &.practice-box-strock {
  297. display: flex;
  298. align-items: center;
  299. justify-content: center;
  300. padding-top: 0;
  301. }
  302. }
  303. }
  304. </style>