writeTable.vue 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658
  1. <template>
  2. <div :class="['writeTable']" v-if="data" v-loading="loading">
  3. <div class="writeTop" :class="[data.fileList ? 'writeTop-nopadding' : '']">
  4. <template v-if="data.fileList">
  5. <UploadDrag
  6. :fileList="data.fileList"
  7. @changeFillId="changeFillId"
  8. v-if="data.fileList.length === 0"
  9. :disabled="is_preview"
  10. ></UploadDrag>
  11. <div class="item-image" v-else>
  12. <el-image
  13. style="width: 593px; height: 224px"
  14. :src="data.fileList[0].fileUrl"
  15. :preview-src-list="[data.fileList[0].fileUrl]"
  16. fit="cover"
  17. />
  18. <span v-if="!is_preview" class="item-image-del" @click="handleDeleteImg"><i class="el-icon-delete"></i></span>
  19. </div>
  20. <div class="item-info">
  21. <template v-if="data.hz_info.length === 1">
  22. <div class="item-info-left">
  23. <p v-if="dataConfig.wordPinyin || dataConfig.wordVoice" class="voice-box">
  24. <AudioPlay :file-id="audio_file" v-if="audio_file" />
  25. <span v-if="dataConfig.wordPinyin">{{ data.pinyin }}</span>
  26. </p>
  27. <div class="hz-box">
  28. <Strockplay
  29. className="adult-strockplay"
  30. :strokePlayColor="dataConfig.borderColor"
  31. :Book_text="itemh.con"
  32. :playStorkes="dataConfig.playStorkes"
  33. :strokeColor="dataConfig.fontColor"
  34. :palyWidth="dataConfig.playWidth"
  35. :BoxbgType="dataConfig.BoxbgType"
  36. :curItem="itemh.hzDetail.hz_json"
  37. :targetDiv="'writeTops-item-' + pageNumber + '-' + indexh + '-' + itemh.con"
  38. :fontSize="fontSize"
  39. :class="[indexh !== 0 ? 'writeTop-item-noLeft' : '']"
  40. class="writeTop-item"
  41. v-for="(itemh, indexh) in data.hz_info"
  42. :key="indexh"
  43. />
  44. </div>
  45. </div>
  46. <div class="item-info-right">
  47. <p class="voice-box" v-if="dataConfig.wordPinyin || dataConfig.wordVoice"></p>
  48. <div class="item-info-row">
  49. <div class="item-info-half">
  50. <label>部首:</label>
  51. <div>{{ data.radical }}</div>
  52. </div>
  53. <div class="item-info-half">
  54. <label>笔画:</label>
  55. {{ data.hz_info[0].hzDetail.hz_json.medians.length }}
  56. </div>
  57. </div>
  58. <template v-if="is_preview">
  59. <div class="item-info-row">
  60. <div class="item-info-half">
  61. <label>释义:</label>
  62. {{ data.info.definition }}
  63. </div>
  64. <div class="item-info-half">
  65. <label>搭配:</label>
  66. {{ data.info.collocation }}
  67. </div>
  68. </div>
  69. <div class="item-info-row">
  70. <div class="item-info-all">{{ data.info.exampleSent }}</div>
  71. </div>
  72. </template>
  73. <template v-else>
  74. <div class="item-info-row">
  75. <el-input
  76. v-model="data.info.definition"
  77. placeholder="输入释义"
  78. @blur="changeInfoObj('definition')"
  79. ></el-input>
  80. <el-input
  81. v-model="data.info.collocation"
  82. placeholder="输入搭配"
  83. @blur="changeInfoObj('collocation')"
  84. ></el-input>
  85. </div>
  86. <div class="item-info-row">
  87. <el-input
  88. v-model="data.info.exampleSent"
  89. placeholder="输入例句"
  90. @blur="changeInfoObj('exampleSent')"
  91. ></el-input>
  92. </div>
  93. </template>
  94. </div>
  95. </template>
  96. <template v-else>
  97. <div class="item-info-left item-info-left-long">
  98. <p v-if="dataConfig.wordPinyin || dataConfig.wordVoice" class="voice-box">
  99. <AudioPlay :file-id="audio_file" v-if="audio_file" />
  100. <span v-if="dataConfig.wordPinyin">{{ data.pinyin }}</span>
  101. </p>
  102. <div class="hz-box" :style="{ margin: '0 auto 4px auto' }">
  103. <Strockplay
  104. className="adult-strockplay"
  105. :strokePlayColor="dataConfig.borderColor"
  106. :Book_text="itemh.con"
  107. :playStorkes="dataConfig.playStorkes"
  108. :strokeColor="dataConfig.fontColor"
  109. :palyWidth="dataConfig.playWidth"
  110. :BoxbgType="dataConfig.BoxbgType"
  111. :curItem="itemh.hzDetail.hz_json"
  112. :targetDiv="'writeTops-item-' + pageNumber + '-' + indexh + '-' + itemh.con"
  113. :fontSize="fontSize"
  114. :class="[indexh !== 0 ? 'writeTop-item-noLeft' : '']"
  115. class="writeTop-item writeTop-item-small"
  116. v-for="(itemh, indexh) in data.hz_info"
  117. :key="indexh"
  118. />
  119. </div>
  120. <template v-if="is_preview">
  121. <div class="item-info-row" :style="{ height: '48px' }">
  122. <div class="item-info-all">{{ data.info.definition }}</div>
  123. </div>
  124. </template>
  125. <el-input
  126. v-else
  127. type="textarea"
  128. v-model="data.info.definition"
  129. rows="2"
  130. placeholder="输入"
  131. @blur="changeInfoObj('definition')"
  132. ></el-input>
  133. </div>
  134. </template>
  135. </div>
  136. </template>
  137. <div
  138. class="writeTop-row"
  139. v-for="(itemR, indexR) in data.list"
  140. :key="'writeTop-' + indexR"
  141. :style="{ marginBottom: dataConfig.marginBottom }"
  142. >
  143. <div
  144. class="writeTop-item"
  145. :class="[indexI !== 0 ? 'writeTop-item-noLeft' : '']"
  146. v-for="(itemI, indexI) in itemR"
  147. :key="'writeTop-item' + indexI"
  148. :style="{
  149. width: dataConfig.width,
  150. height: dataConfig.width,
  151. borderColor: dataConfig.borderColor,
  152. }"
  153. >
  154. <template v-if="itemI && itemI.con && itemI.isPunctuation">
  155. <!-- 不是汉字的内容 -->
  156. <div class="punctuation-box">
  157. <div class="character-target-div">
  158. <svg-icon
  159. icon-class="tian"
  160. className="tian-bg"
  161. v-if="dataConfig.BoxbgType == 0"
  162. :style="{ color: '#DEDEDE' }"
  163. />
  164. <svg-icon
  165. icon-class="mi"
  166. className="tian-bg"
  167. v-if="dataConfig.BoxbgType == 1"
  168. :style="{ color: '#DEDEDE' }"
  169. />
  170. </div>
  171. <h3
  172. :style="{
  173. lineHeight: dataConfig.width,
  174. color: itemI.miaoRed ? dataConfig.writeColor : dataConfig.fontColor,
  175. fontSize: dataConfig.fontSize,
  176. }"
  177. >
  178. {{ itemI.con }}
  179. </h3>
  180. </div>
  181. </template>
  182. <template v-else-if="itemI && itemI.con && !itemI.write && !itemI.answer && !itemI.miaoRed">
  183. <Strockplay
  184. v-if="'writeTop-item-' + pageNumber + '-' + indexI + type"
  185. className="adult-strockplay"
  186. :strokePlayColor="dataConfig.borderColor"
  187. :Book_text="itemI.con"
  188. :playStorkes="dataConfig.playStorkes"
  189. :strokeColor="dataConfig.fontColor"
  190. :palyWidth="dataConfig.playWidth"
  191. :BoxbgType="dataConfig.BoxbgType"
  192. :curItem="itemI.hzDetail"
  193. :targetDiv="'writeTop-item-' + pageNumber + '-' + indexR + '-' + indexI + type + itemI.con"
  194. :fontSize="fontSize"
  195. />
  196. </template>
  197. <template v-else-if="itemI && itemI.answer">
  198. <StrockplayredlineTable
  199. :Book_text="itemI.con"
  200. :playStorkes="false"
  201. :strokeColorgray="dataConfig.miaoRedBgcolor"
  202. :strokeColor="dataConfig.fontColor"
  203. :strokeNumber="itemI.answer"
  204. :curItem="itemI.hzDetail"
  205. :BoxbgType="dataConfig.BoxbgType"
  206. :targetDiv="'write-item-miaohong-' + pageNumber + '-' + indexR + '-' + indexI + type + itemI.con"
  207. :targetDivGray="'write-item-miaohong-gray-' + pageNumber + '-' + indexR + '-' + indexI + type + itemI.con"
  208. :fontSize="fontSize"
  209. />
  210. </template>
  211. <template v-else-if="itemI && itemI.miaoRed">
  212. <Strockred
  213. v-if="'write-item-' + pageNumber + '-' + indexI + type"
  214. className="adult-strockplay"
  215. :strokePlayColor="dataConfig.borderColor"
  216. :Book_text="itemI.con"
  217. :curItem="itemI.hzDetail"
  218. :hanzicolor="dataConfig.miaoRedBgcolor"
  219. :drawingColor="dataConfig.writeColor"
  220. :BoxbgType="dataConfig.BoxbgType"
  221. :targetDiv="'write-item-' + pageNumber + '-' + indexR + '-' + indexI + type + itemI.con"
  222. :fontSize="fontSize"
  223. />
  224. </template>
  225. <div v-else-if="itemI" class="tian-div" @click="freeWrite(itemI, indexR, indexI)">
  226. <svg-icon
  227. icon-class="tian"
  228. className="tian"
  229. v-if="dataConfig.BoxbgType == 0"
  230. :style="{ color: '#DEDEDE' }"
  231. />
  232. <svg-icon icon-class="mi" className="tian" v-if="dataConfig.BoxbgType == 1" :style="{ color: '#DEDEDE' }" />
  233. <img v-if="itemI && itemI.strokes_image_url" :src="itemI.strokes_image_url" alt="" />
  234. </div>
  235. </div>
  236. </div>
  237. </div>
  238. <!-- <div class="writeBottom">
  239. <span>BLCUP 全球国际中文教学云平台</span>
  240. <b>{{ pageNumber + '/' + totalNumber }}</b>
  241. <a>www.chinesedu.com</a>
  242. </div> -->
  243. <div class="practiceBox practiceBoxStrock" v-if="ifFreeShow">
  244. <FreewriteLettle
  245. :changePraShow="changePraShow"
  246. ref="freePaint"
  247. :currenHzData="currenHzData"
  248. :rowIndex="activeIndex"
  249. :colIndex="activeColIndex"
  250. :closeifFreeShow="closeifFreeShow"
  251. @ExerciseChangeCurQue="ExerciseChangeCurQue"
  252. :BoxbgType="dataConfig.BoxbgType"
  253. :lineColor="dataConfig.writeColor"
  254. />
  255. </div>
  256. </div>
  257. </template>
  258. <script>
  259. //这里可以导入其它文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
  260. import StrockplayredlineTable from '../../components/corpus/StrockplayredlineTable.vue';
  261. import Strockplay from '../../components/corpus/Strockplay.vue';
  262. import Strockred from '../../components/corpus/Strockred.vue';
  263. import FreewriteLettle from '../../components/corpus/FreewriteLettle.vue';
  264. import UploadDrag from './UploadDrag.vue';
  265. import AudioPlay from './AudioPlay.vue';
  266. const HanziWriter = require('hanzi-writer');
  267. import { getLogin } from '@/api/api';
  268. export default {
  269. //import引入的组件需要注入到对象中才能使用
  270. components: {
  271. StrockplayredlineTable,
  272. Strockplay,
  273. Strockred,
  274. FreewriteLettle,
  275. UploadDrag,
  276. AudioPlay,
  277. },
  278. props: [
  279. 'dataConfig',
  280. 'data',
  281. 'pageNumber',
  282. 'totalNumber',
  283. 'type',
  284. 'none',
  285. 'fontSize',
  286. 'audio_file_obj',
  287. 'is_preview',
  288. 'infoObj',
  289. ],
  290. data() {
  291. //这里存放数据
  292. return {
  293. ifFreeShow: false,
  294. activeIndex: null,
  295. activeColIndex: null,
  296. fileList: [],
  297. info: {
  298. definition: '',
  299. collocation: '',
  300. exampleSent: '',
  301. },
  302. writer: null,
  303. audio_file: '',
  304. loading: false,
  305. };
  306. },
  307. //计算属性 类似于data概念
  308. computed: {},
  309. //监控data中数据变化
  310. watch: {
  311. pageNumber: {
  312. handler: function (val, oldVal) {
  313. if (val != oldVal) {
  314. this.initHanziwrite();
  315. }
  316. },
  317. deep: true,
  318. },
  319. },
  320. //方法集合
  321. methods: {
  322. changePraShow() {
  323. this.ifFreeShow = false;
  324. },
  325. closeifFreeShow(data, rowIndex, colIndex) {
  326. this.ifFreeShow = false;
  327. this.$forceUpdate();
  328. },
  329. freeWrite(item, row, col) {
  330. this.ifFreeShow = true;
  331. this.activeIndex = row;
  332. this.activeColIndex = col;
  333. // this.currentHz = this.curQue.option[indexs].rightOption[rightindex].con;
  334. if (item) {
  335. this.currenHzData = item;
  336. } else {
  337. this.currenHzData = {};
  338. }
  339. },
  340. ExerciseChangeCurQue(answer, rowIndex, colIndex) {
  341. if (answer) {
  342. this.data.list[rowIndex][colIndex].strokes_image_url = answer.strokes_image_url;
  343. this.data.list[rowIndex][colIndex].history = answer.history;
  344. this.$forceUpdate();
  345. }
  346. },
  347. changeFillId(file, fileList) {
  348. let obj = {
  349. name: file.name,
  350. fileId: file.file_id,
  351. fileUrl: file.file_url_open,
  352. };
  353. this.data.fileList.push(obj);
  354. this.infoObj[this.data.con].fileList = [obj];
  355. },
  356. handleDeleteImg() {
  357. this.data.fileList = [];
  358. this.infoObj[this.data.con].fileList = [];
  359. },
  360. initHanziwrite() {
  361. // let _this = this;
  362. // let node = document.getElementById(`character-target-info-div` + _this.pageNumber);
  363. // if (node && node.children.length > 1) {
  364. // node.removeChild(node.children[1]);
  365. // }
  366. // _this.writer = HanziWriter.default.create(
  367. // `character-target-info-div` + _this.pageNumber,
  368. // _this.data.hz_info[0].con,
  369. // {
  370. // width: 22,
  371. // height: 22,
  372. // padding: 0,
  373. // radicalColor: '#000000',
  374. // strokeColor: '#fff',
  375. // },
  376. // );
  377. if (this.data.con) {
  378. this.loading = true;
  379. let MethodName = 'tool-TextToVoiceFile';
  380. let datas = {
  381. text: this.data.con,
  382. };
  383. getLogin(MethodName, datas)
  384. .then((res) => {
  385. this.loading = false;
  386. if (res.status === 1) {
  387. this.audio_file = res.file_id;
  388. }
  389. })
  390. .catch(() => {
  391. this.loading = false;
  392. });
  393. }
  394. },
  395. changeInfoObj(param) {
  396. this.infoObj[this.data.con][param] = this.data.info[param] ? this.data.info[param].trim() : '';
  397. },
  398. },
  399. //生命周期 - 创建完成(可以访问当前this实例)
  400. created() {
  401. this.initHanziwrite();
  402. },
  403. //生命周期 - 挂载完成(可以访问DOM元素)
  404. mounted() {
  405. // let _this = this;
  406. // _this.$nextTick(() => {
  407. // if (_this.data.hz_info && _this.data.hz_info.length === 1) {
  408. // _this.initHanziwrite();
  409. // }
  410. // });
  411. },
  412. //生命周期-创建之前
  413. beforeCreated() {},
  414. //生命周期-挂载之前
  415. beforeMount() {},
  416. //生命周期-更新之前
  417. beforUpdate() {},
  418. //生命周期-更新之后
  419. updated() {},
  420. //生命周期-销毁之前
  421. beforeDestory() {},
  422. //生命周期-销毁完成
  423. destoryed() {},
  424. //如果页面有keep-alive缓存功能,这个函数会触发
  425. activated() {},
  426. };
  427. </script>
  428. <style lang="scss" scoped>
  429. .writeTable {
  430. background: #ffffff;
  431. border: 1px solid rgba(0, 0, 0, 0.08);
  432. width: 595px;
  433. height: 842px;
  434. box-sizing: border-box;
  435. .writeTop {
  436. height: 794px;
  437. padding-top: 48px;
  438. .item-image {
  439. position: relative;
  440. .item-image-del {
  441. position: absolute;
  442. top: 8px;
  443. right: 8px;
  444. width: 16px;
  445. height: 16px;
  446. display: block;
  447. cursor: pointer;
  448. background-color: #ffffff;
  449. color: #ee3232;
  450. padding: 8px;
  451. border-radius: 50%;
  452. box-shadow: 0px 4px 4px 0px rgba(0, 0, 0, 0.25);
  453. }
  454. }
  455. .writeTop-row {
  456. display: flex;
  457. justify-content: center;
  458. }
  459. }
  460. .writeTop-nopadding {
  461. padding-top: 0;
  462. height: 842px;
  463. }
  464. .item-info {
  465. display: flex;
  466. width: 100%;
  467. padding: 0 46px 8px 46px;
  468. column-gap: 16px;
  469. box-sizing: border-box;
  470. &-left {
  471. .writeTop-item {
  472. width: 98px;
  473. height: 98px;
  474. :deep .strock-play-box {
  475. width: 18px !important;
  476. height: 18px !important;
  477. }
  478. :deep .playStorkes-btn {
  479. width: 18px !important;
  480. height: 18px !important;
  481. }
  482. &-small {
  483. width: 62px;
  484. height: 62px;
  485. :deep .strock-play-box {
  486. width: 11px !important;
  487. height: 11px !important;
  488. }
  489. :deep .playStorkes-btn {
  490. width: 11px !important;
  491. height: 11px !important;
  492. }
  493. }
  494. }
  495. &-long {
  496. width: 100%;
  497. }
  498. }
  499. &-right {
  500. flex: 1;
  501. }
  502. :deep .el-textarea__inner {
  503. resize: none;
  504. background-color: #f3f3f3;
  505. border: none;
  506. outline: none;
  507. }
  508. .voice-box {
  509. width: 100%;
  510. height: 32px;
  511. display: flex;
  512. align-items: center;
  513. justify-content: center;
  514. column-gap: 4px;
  515. img {
  516. width: 24px;
  517. height: 24px;
  518. }
  519. span {
  520. font-family: League;
  521. font-size: 16px;
  522. font-weight: 400;
  523. color: #de4444;
  524. }
  525. }
  526. .item-info-row {
  527. display: flex;
  528. column-gap: 11px;
  529. margin-bottom: 6px;
  530. :deep .el-input__inner {
  531. background-color: #f3f3f3;
  532. border: none;
  533. outline: none;
  534. height: 32px;
  535. }
  536. }
  537. .item-info-half,
  538. .item-info-all {
  539. width: 50%;
  540. display: flex;
  541. font-size: 14px;
  542. line-height: 22px;
  543. height: 22px;
  544. }
  545. .item-info-all {
  546. width: 100%;
  547. }
  548. }
  549. .hz-box {
  550. display: flex;
  551. width: max-content;
  552. }
  553. .writeTop-item {
  554. border: 1px solid #de4444;
  555. }
  556. .writeTop-item-noLeft {
  557. border-left: none;
  558. }
  559. .writeBottom {
  560. display: flex;
  561. width: 100%;
  562. justify-content: space-between;
  563. padding: 0 38px;
  564. box-sizing: border-box;
  565. position: relative;
  566. span {
  567. font-weight: 500;
  568. font-size: 10px;
  569. line-height: 14px;
  570. color: #000000;
  571. opacity: 0.2;
  572. }
  573. b {
  574. font-weight: 400;
  575. font-size: 14px;
  576. line-height: 14px;
  577. color: #000000;
  578. width: 60px;
  579. text-align: center;
  580. position: absolute;
  581. left: 50%;
  582. margin-left: -30px;
  583. top: 0px;
  584. }
  585. a {
  586. font-weight: 500;
  587. font-size: 12px;
  588. line-height: 14px;
  589. color: #000000;
  590. opacity: 0.2;
  591. }
  592. }
  593. .tian-div {
  594. width: 100%;
  595. height: 100%;
  596. position: relative;
  597. .tian {
  598. width: 100%;
  599. height: 100%;
  600. }
  601. img {
  602. width: 100%;
  603. height: 100%;
  604. position: absolute;
  605. left: 0;
  606. top: 0;
  607. }
  608. }
  609. .practiceBox {
  610. position: fixed;
  611. left: 0;
  612. top: 0;
  613. z-index: 100100;
  614. width: 100%;
  615. height: 100vh;
  616. background: rgba(0, 0, 0, 0.19);
  617. box-sizing: border-box;
  618. overflow: hidden;
  619. overflow-y: auto;
  620. &.practiceBoxStrock {
  621. display: flex;
  622. justify-content: center;
  623. align-items: center;
  624. padding-top: 0px;
  625. }
  626. }
  627. .punctuation-box {
  628. position: relative;
  629. width: 100%;
  630. height: 100%;
  631. .character-target-div {
  632. position: absolute;
  633. width: 100%;
  634. height: 100%;
  635. top: 0;
  636. left: 0;
  637. .svg-icon {
  638. width: 100%;
  639. height: 100%;
  640. }
  641. }
  642. h3 {
  643. position: absolute;
  644. width: 100%;
  645. height: 100%;
  646. top: 0;
  647. left: 0;
  648. font-family: FZJCGFKTK;
  649. text-align: center;
  650. font-weight: normal;
  651. }
  652. }
  653. }
  654. </style>