Freewrite.vue 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. <!-- -->
  2. <template>
  3. <div class="freewrite">
  4. <div class="strockred" :class="wordNum ? '' : 'strockred_single'">
  5. <div :class="wordNum ? 'character-target-div_more' : 'character-target-div_single'" class="character-target-div">
  6. <FreeWriteQP
  7. id="esign"
  8. ref="esign"
  9. :bg-color.sync="bgColor"
  10. :height="height"
  11. :is-crop="isCrop"
  12. :line-color="lineColor"
  13. :line-width="lineWidth"
  14. :width="width"
  15. class="vueEsign"
  16. />
  17. </div>
  18. </div>
  19. <div class="imgsave">
  20. <div class="image">
  21. <div
  22. v-for="(item, i) in imgarr"
  23. :key="'img' + i"
  24. :class="['character-target-div', i == imgIndex ? 'select' : '']"
  25. @click="play(i)"
  26. >
  27. <img class="img" :src="item.strokes_image_url" alt="" />
  28. </div>
  29. </div>
  30. <div v-if="TaskModel != 'ANSWER'" style="display: flex">
  31. <div v-if="imgarr.length > 0" class="xj" style="margin-right: 8px" @click="removeImage">
  32. <img src="@/assets/word-remove.png" alt="" />
  33. </div>
  34. <!--
  35. @mouseover="saveShow = true"
  36. @mouseout="saveShow = false"
  37. -->
  38. <div :class="['xj', saveShow ? 'click' : '']">
  39. <img
  40. :src="saveShow ? require('@/assets/xj-xz.png') : require('@/assets/xj.png')"
  41. alt=""
  42. @click="handleGenerate"
  43. />
  44. </div>
  45. </div>
  46. </div>
  47. <div v-if="isNotice" class="record-notice">最多保存5条记录</div>
  48. </div>
  49. </template>
  50. <script>
  51. import vueEsign from 'vue-esign';
  52. import FreeWriteQP from './FreeWriteQP.vue';
  53. import { LearnWebSI } from '@/api/app';
  54. export default {
  55. components: {
  56. FreeWriteQP,
  57. vueEsign,
  58. },
  59. props: ['lineColor', 'lineWidth', 'cur', 'wordNum', 'currentTreeID', 'TaskModel', 'writeList'],
  60. data() {
  61. return {
  62. width: this.wordNum != '2' ? 256 : 256,
  63. height: 256,
  64. bgColor: '',
  65. isCrop: false,
  66. history: [],
  67. imgarr: [],
  68. imgIndex: null,
  69. saveShow: false,
  70. tabIndex: 0,
  71. hasPlay: false,
  72. isNotice: false,
  73. };
  74. },
  75. computed: {},
  76. watch: {
  77. lineColor(newVal, oldVal) {
  78. this.updateColor(newVal);
  79. },
  80. },
  81. // 生命周期 - 创建完成(可以访问当前this实例)
  82. created() {},
  83. // 生命周期 - 挂载完成(可以访问DOM元素)
  84. mounted() {
  85. // this.getImgList();
  86. },
  87. beforeCreate() {}, // 生命周期 - 创建之前
  88. beforeMount() {}, // 生命周期 - 挂载之前
  89. beforeUpdate() {}, // 生命周期 - 更新之前
  90. updated() {}, // 生命周期 - 更新之后
  91. beforeDestroy() {}, // 生命周期 - 销毁之前
  92. destroyed() {}, // 生命周期 - 销毁完成
  93. activated() {},
  94. // 方法集合
  95. methods: {
  96. getImgList(tabIndex) {
  97. this.tabIndex = tabIndex;
  98. this.imgarr = [];
  99. let MethodName = 'teaching-practice_manager-GetMyHZHandwrittenRecordList';
  100. let hz = this.tabIndex == 0 ? this.cur.stem[0].con : this.cur.stem[0].TChinese;
  101. let data = {
  102. courseware_id: this.currentTreeID,
  103. hz,
  104. search_scope: 1,
  105. count_limit: 5,
  106. };
  107. LearnWebSI(MethodName, data).then((res) => {
  108. let imgarr = res.hz_handwritten_record_list;
  109. this.imgarr = imgarr.map((item) => {
  110. item.history = JSON.parse(item.strokes_content);
  111. return item;
  112. });
  113. });
  114. },
  115. getStuImgList(tabIndex) {
  116. this.tabIndex = tabIndex;
  117. this.imgarr = [];
  118. let hz = this.tabIndex == 0 ? this.cur.stem[0].con : this.cur.stem[0].TChinese;
  119. let imgarr = JSON.parse(JSON.stringify(this.writeList));
  120. this.imgarr = imgarr[hz].map((item) => {
  121. item.history = JSON.parse(item.strokes_content);
  122. return item;
  123. });
  124. },
  125. removeImage() {
  126. if (this.hasPlay) {
  127. this.$message.warning('正在播放,不能删除');
  128. return;
  129. }
  130. if (this.imgIndex === null) return;
  131. let MethodName = 'teaching-practice_manager-DeleteMyHZHandwrittenRecord';
  132. let data = {
  133. hz_handwritten_record_id: this.imgarr[this.imgIndex].hz_handwritten_record_id,
  134. };
  135. LearnWebSI(MethodName, data).then((res) => {
  136. this.$message.success('删除成功');
  137. this.imgarr.splice(this.imgIndex, 1);
  138. this.handelReset();
  139. });
  140. },
  141. play(index) {
  142. if (this.hasPlay) {
  143. this.$message.warning('请等待播放完成');
  144. return;
  145. }
  146. this.$refs.esign.reset();
  147. this.hasPlay = true;
  148. this.imgIndex = index;
  149. let c = document.getElementById('esign');
  150. let cxt = document.getElementById('esign').getContext('2d');
  151. cxt.clearRect(0, 0, c.width, c.height);
  152. let history = this.imgarr[index].history;
  153. const len = history.length;
  154. let i = 0;
  155. const runner = () => {
  156. i++;
  157. if (i < len) {
  158. this.$refs.esign.draw(null, history[i][0], history[i][1]);
  159. requestAnimationFrame(runner);
  160. } else {
  161. console.log('播放完成');
  162. this.hasPlay = false;
  163. }
  164. };
  165. requestAnimationFrame(runner);
  166. },
  167. handelReset() {
  168. this.$refs.esign.reset();
  169. this.imgIndex = null;
  170. },
  171. handleGenerate() {
  172. let _this = this;
  173. if (_this.imgarr.length >= 5) {
  174. _this.isNotice = true;
  175. setTimeout(() => {
  176. _this.isNotice = false;
  177. }, 2000);
  178. return;
  179. }
  180. this.saveShow = true;
  181. let hz = this.tabIndex == 0 ? this.cur.stem[0].con : this.cur.stem[0].TChinese;
  182. this.$refs.esign
  183. .generate()
  184. .then((res) => {
  185. let Book_img = res.replace('data:image/png;base64,', '');
  186. let write_img = `data:image/png;base64,${ Book_img}`;
  187. let answer = {};
  188. answer = {
  189. hz,
  190. strokes_content: JSON.stringify(this.$refs.esign.history),
  191. strokes_image_url: write_img,
  192. };
  193. this.$emit('saveWriteAnswer', answer);
  194. let obj = {
  195. hz_handwritten_record_id: '',
  196. history: this.$refs.esign.history,
  197. strokes_image_url: write_img,
  198. };
  199. let data = {
  200. courseware_id: this.currentTreeID,
  201. hz,
  202. strokes_content: JSON.stringify(this.$refs.esign.history),
  203. strokes_image_base64: Book_img,
  204. };
  205. let MethodName = 'teaching-practice_manager-SaveMyHZHandwrittenRecord';
  206. LearnWebSI(MethodName, data)
  207. .then((res) => {
  208. if (res.status === 1) {
  209. this.$message.success('保存成功!');
  210. obj.hz_handwritten_record_id = res.hz_handwritten_record_id;
  211. }
  212. this.imgarr.push(obj);
  213. this.saveShow = false;
  214. this.handelReset();
  215. })
  216. .catch((res) => {
  217. console.log(res);
  218. this.imgarr.push(obj);
  219. this.saveShow = false;
  220. this.handelReset();
  221. });
  222. // console.log(Book_img);
  223. // this.textOcr(res.replace("data:image/png;base64,", ""));
  224. })
  225. .catch((err) => {
  226. this.saveShow = false;
  227. console.log(err);
  228. this.$message.warning('请先书写在保存');
  229. });
  230. },
  231. }, // 如果页面有keep-alive缓存功能,这个函数会触发
  232. };
  233. </script>
  234. <style lang="scss" scoped>
  235. //@import url(); 引入公共css类
  236. .freewrite {
  237. position: relative;
  238. width: 100%;
  239. .record-notice {
  240. position: absolute;
  241. top: 106px;
  242. left: 56px;
  243. z-index: 999999;
  244. box-sizing: border-box;
  245. height: 40px;
  246. padding: 0 20px;
  247. font-size: 14px;
  248. line-height: 38px;
  249. color: #fff;
  250. background: rgba(0, 0, 0, 80%);
  251. border: 1px solid rgba(0, 0, 0, 100%);
  252. border-radius: 4px;
  253. }
  254. .imgsave {
  255. display: flex;
  256. justify-content: space-between;
  257. margin-top: 8px;
  258. .image {
  259. display: flex;
  260. > div {
  261. width: 28px;
  262. height: 28px;
  263. margin-right: 8px;
  264. background: #fff url('@/assets/chinaTianRed.png') center no-repeat;
  265. background-size: 100% 100%;
  266. border-radius: 4px;
  267. }
  268. .select {
  269. background: rgba(222, 68, 68, 10%);
  270. border: 1px solid #f26c6c;
  271. }
  272. .img {
  273. width: 100%;
  274. height: 100%;
  275. cursor: pointer;
  276. }
  277. }
  278. .xj {
  279. display: flex;
  280. align-items: center;
  281. justify-content: center;
  282. width: 28px;
  283. height: 28px;
  284. cursor: pointer;
  285. background: rgba(0, 0, 0, 4%);
  286. border-radius: 4px;
  287. img {
  288. width: 16px;
  289. height: 16px;
  290. }
  291. }
  292. .click {
  293. background: rgba(222, 68, 68, 10%);
  294. }
  295. }
  296. .strockred {
  297. position: relative;
  298. width: 100%;
  299. height: 256px;
  300. margin: 0 auto;
  301. .character-target-div {
  302. z-index: 99999;
  303. display: flex;
  304. align-items: center;
  305. justify-content: center;
  306. width: 100%;
  307. height: 256px;
  308. background: #fff url('@/assets/chinaTianRed.png') center no-repeat;
  309. background-size: 100% 100%;
  310. border-radius: 24px;
  311. &_more {
  312. width: 100%;
  313. background: 0 0;
  314. background-size: 100% 100%;
  315. }
  316. .vueEsign {
  317. width: 256px;
  318. height: 256px;
  319. }
  320. }
  321. }
  322. }
  323. </style>