right.vue 28 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009
  1. <template>
  2. <div class="o_box" ref="obox">
  3. <div class="o_top">
  4. </div>
  5. <div class="o_content">
  6. <div class="type_box" :style="{ width: oWidth }" v-if="cjson.type !== 'createRole'">
  7. {{ getType(cjson) }}
  8. </div>
  9. <div class="word_box" v-if="cjson.type == 'word' || cjson.type == 'QA'" ref="wb">
  10. <div class="word_bbox" :style="{ maxHeight: oheight }">
  11. <img class="word_img" :src="cjson.img" alt="" v-if="cjson.img" @click="previewImg(cjson.img)">
  12. <div class="word_content" v-html="cjson.content">
  13. </div>
  14. </div>
  15. </div>
  16. <div class="sentence_box" v-if="cjson.type == 'sentence'" ref="wb">
  17. <span v-html="cjson.content"></span>
  18. <div v-if="cjson.img" class="sentence_div">
  19. <img :src="cjson.img" alt="" @click="previewImg(cjson.img)">
  20. </div>
  21. </div>
  22. <div class="word_box" v-if="cjson.type == 'theme'" ref="wb" style="max-height: calc(100% - 95px);">
  23. <div class="word_bbox" :style="{ maxHeight: oheight }">
  24. <div class="word_content" v-html="cjson.content"></div>
  25. <div class="word_content2" v-html="cjson.content2" v-if="cjson.content2"></div>
  26. </div>
  27. </div>
  28. <div class="tips_box" v-if="cjson.type == 'theme' && !isRecord && !LuAudioUrl">提示:准备完成后,点击话筒开始录音</div>
  29. <div class="time_box" v-if="cjson.type == 'theme' && isRecord">
  30. <span>倒计时</span>
  31. <span>{{ Times.min }}:{{ Times.secode }}</span>
  32. </div>
  33. <testRole v-if="cjson.type == 'createRole'" :checkJson="answerArray"></testRole>
  34. </div>
  35. <div class="o_bottom" v-loading="isloading">
  36. <div class="star_box" v-if="LuAudioUrl && star > 0">
  37. <div class="star" v-for="index2 in 5" :key="'star' + index2" :class="{ starA: star >= (index2) }">
  38. </div>
  39. </div>
  40. <div class="audio" v-if="!LuAudioUrl">
  41. <img v-if="!isRecord" src="../../../assets/icon/englishVoice/start_aduio.png" alt="" @click="startRecorder">
  42. <img v-else src="../../../assets/icon/englishVoice/stop_audio.png" alt="" @click="startRecorder">
  43. </div>
  44. <div class="audio_word" v-if="!LuAudioUrl">
  45. <span v-if="!isRecord">点击话筒开始录音</span>
  46. <span v-else>点击话筒结束录音</span>
  47. </div>
  48. <div v-if="LuAudioUrl" class="audio_b">
  49. <mini-audio :audio-source="LuAudioUrl" class="audio_class"></mini-audio>
  50. </div>
  51. <div v-if="LuAudioUrl" class="audio_rerecord" @click="restart()">
  52. <span>录音</span>
  53. </div>
  54. <div class="audio_index" v-if="!isRecord">
  55. <div class="audio_index_last" :class="{ disabled: checkType == 0 }" @click="checkIndex('-1')">
  56. <img src="../../../assets/icon/englishVoice/coin.png" alt="">
  57. </div>
  58. <div class="audio_index_content">
  59. <span>{{ checkType + 1 }}</span>
  60. <span>/</span>
  61. <span>{{ checkJson.length }}</span>
  62. </div>
  63. <div class="audio_index_next" :class="{ disabled: checkType == (checkJson.length - 1) }"
  64. @click="checkIndex('1')">
  65. <img src="../../../assets/icon/englishVoice/coin.png" alt="">
  66. </div>
  67. </div>
  68. <div v-else class="audio_ing">
  69. <span>正在录音...</span>
  70. </div>
  71. </div>
  72. <iframe allow="camera *; microphone *;display-capture;midi;encrypted-media;"
  73. src="https://beta.cloud.cocorobo.cn/browser/public/index.html" ref="iiframe" v-show="false"></iframe>
  74. </div>
  75. </template>
  76. <script>
  77. import Recorder from "js-audio-recorder";
  78. const lamejs = require("lamejs");
  79. const recorder = new Recorder({
  80. sampleBits: 16, // 采样位数,支持 8 或 16,默认是16
  81. sampleRate: 48000, // 采样率,支持 11025、16000、22050、24000、44100、48000,根据浏览器默认值,我的chrome是48000
  82. numChannels: 1, // 声道,支持 1 或 2, 默认是1
  83. // compiling: false,(0.x版本中生效,1.x增加中) // 是否边录边转换,默认是false
  84. });
  85. // 绑定事件-打印的是当前录音数据
  86. recorder.onprogress = function (params) {
  87. // console.log('--------------START---------------')
  88. // console.log('录音时长(秒)', params.duration);
  89. // console.log('录音大小(字节)', params.fileSize);
  90. // console.log('录音音量百分比(%)', params.vol);
  91. // console.log('当前录音的总数据([DataView, DataView...])', params.data);
  92. // console.log('--------------END---------------')
  93. };
  94. import testRole from "./testRole.vue";
  95. export default {
  96. components: {
  97. testRole,
  98. },
  99. props: {
  100. checkJson: {
  101. type: Array,
  102. },
  103. checkType: {
  104. type: Number,
  105. },
  106. work: {
  107. type: Array
  108. }
  109. },
  110. data() {
  111. return {
  112. cjson: {},
  113. LuAudioUrl: "",
  114. isRecord: false,
  115. isPlayerRecord: false,
  116. isloading: false,
  117. oheight: 'auto',
  118. oWidth: '500px',
  119. calcTimer: null,
  120. totalSeconds: 0,
  121. answerArray: [],
  122. id: this.guid(),
  123. star: 0
  124. }
  125. },
  126. computed: {
  127. // oheight: function() {
  128. // // 获取父元素
  129. // var parentElement = this.$refs['wb'];
  130. // // 计算父元素的高度
  131. // var parentHeight = parentElement.offsetHeight;
  132. // return parentHeight + 'px'
  133. // }
  134. getType() {
  135. return function (json) {
  136. if (json.type == 'word') {
  137. return '单词/词组'
  138. } else if (json.type == 'QA') {
  139. return '问答题目'
  140. } else if (json.type == 'sentence') {
  141. return '句子/短文'
  142. } else if (json.type == 'theme') {
  143. return '主题陈述'
  144. }
  145. };
  146. },
  147. Times() {
  148. let min = this.totalSeconds ? Math.floor(this.totalSeconds / 60) : 0
  149. let secode = this.totalSeconds ? this.totalSeconds % 60 : 0
  150. let time = {
  151. min: min >= 10 ? min : '0' + min,
  152. secode: secode >= 10 ? secode : '0' + secode
  153. }
  154. return time;
  155. },
  156. },
  157. watch: {
  158. checkType: {
  159. handler: function (newVal, oldVal) {
  160. this.isloading = false
  161. this.cjson = JSON.parse(JSON.stringify(this.checkJson[newVal]));
  162. this.LuAudioUrl = ''
  163. if (typeof this.work[newVal] == 'string') {
  164. this.LuAudioUrl = this.work[newVal] ? JSON.parse(JSON.stringify(this.work[newVal])) : ''
  165. } else if (typeof this.work[newVal] == 'object' && this.cjson.type != 'createRole') {
  166. this.LuAudioUrl = this.work[newVal].audio ? JSON.parse(JSON.stringify(this.work[newVal].audio)) : ''
  167. } else if (typeof this.work[newVal] == 'object' && this.cjson.type == 'createRole') {
  168. var a = Array.isArray(this.work[newVal])
  169. if (a) {
  170. this.answerArray = JSON.parse(JSON.stringify(this.work[newVal]))
  171. } else {
  172. this.answerArray = []
  173. this.answerArray.push(
  174. {
  175. isY: false,
  176. content: this.cjson.content3,
  177. name: this.cjson.content,
  178. img: this.cjson.img
  179. }
  180. )
  181. }
  182. this.$emit('setWork', this.answerArray, this.checkType)
  183. }
  184. if (!this.work[newVal] && this.cjson.type == 'createRole') {
  185. var a = Array.isArray(this.work[newVal])
  186. if (a) {
  187. this.answerArray = JSON.parse(JSON.stringify(this.work[newVal]))
  188. } else {
  189. this.answerArray = []
  190. this.answerArray.push(
  191. {
  192. isY: false,
  193. content: this.cjson.content3,
  194. name: this.cjson.content,
  195. img: this.cjson.img
  196. }
  197. )
  198. }
  199. this.$emit('setWork', this.answerArray, this.checkType)
  200. }
  201. this.star = this.work[newVal] ? (this.work[newVal].score ? JSON.parse(JSON.stringify(this.work[newVal].score)) : 0) : 0
  202. this.oheight = 'auto'
  203. this.oWidth = '500px'
  204. const images = this.$refs['obox'].querySelectorAll('img');
  205. let loadedCount = 0;
  206. // if(images.length){
  207. // images.forEach((image) => {
  208. // image.addEventListener('load', () => {
  209. // loadedCount++;
  210. // if (loadedCount === images.length) {
  211. // this.calculateParentHeight()
  212. // }
  213. // });
  214. // });
  215. // }else{
  216. if (this.cjson.type != "createRole") {
  217. this.calculateParentHeight()
  218. }
  219. if (this.cjson.type == "createRole") {
  220. this.createRole(this.cjson.content2, this.cjson.content)
  221. }
  222. // }
  223. },
  224. deep: true,
  225. },
  226. },
  227. methods: {
  228. previewImg(url) {
  229. this.$hevueImgPreview(url);
  230. },
  231. restart() {
  232. let _this = this
  233. _this.$confirm("确定重新录音么?", "提示", {
  234. confirmButtonText: "确定",
  235. cancelButtonText: "取消",
  236. type: "warning",
  237. })
  238. .then(() => {
  239. _this.LuAudioUrl = ""
  240. setTimeout(() => {
  241. _this.startRecorder()
  242. }, 500);
  243. })
  244. .catch(() => { });
  245. },
  246. checkIndex(type) {
  247. if (type == '1') {
  248. if (this.checkType == (this.checkJson.length - 1)) {
  249. return;
  250. }
  251. this.$emit('setType', this.checkType + 1)
  252. } else {
  253. if (this.checkType == 0) {
  254. return;
  255. }
  256. this.$emit('setType', this.checkType - 1)
  257. }
  258. },
  259. // 开始录音
  260. startRecorder() {
  261. let _this = this;
  262. if (!_this.isRecord) {
  263. recorder.destroy(); // 销毁录音
  264. _this.isRecord = true;
  265. if (this.cjson.type == 'theme') {
  266. this.setSecodes()
  267. }
  268. recorder.start().then(
  269. () => { },
  270. (error) => {
  271. _this.isRecord = false;
  272. // _this.$message.error(`${error.name} : ${error.message}`);
  273. _this.$message.error(`没有找到可使用的麦克风,或者您没有允许此网页使用麦克风`);
  274. // 出错了
  275. console.log(`${error.name} : ${error.message}`);
  276. if (_this.calcTimer) {
  277. clearInterval(_this.calcTimer)
  278. _this.calcTimer = null;
  279. }
  280. }
  281. );
  282. } else {
  283. if (_this.calcTimer) {
  284. clearInterval(_this.calcTimer)
  285. _this.calcTimer = null;
  286. }
  287. _this.isRecord = false;
  288. recorder.stop(); // 结束录音
  289. this.getMp3Data()
  290. }
  291. },
  292. // 录音播放
  293. playRecorder() {
  294. if (!recorder.fileSize) {
  295. return;
  296. }
  297. if (!this.isPlayerRecord) {
  298. this.isPlayerRecord = true;
  299. recorder.play();
  300. } else {
  301. this.isPlayerRecord = false;
  302. recorder.stopPlay(); // 停止录音播放
  303. }
  304. recorder.onplayend = () => {
  305. this.isPlayerRecord = false;
  306. console.log("onplayend");
  307. };
  308. },
  309. /**
  310. * 文件格式转换 wav-map3
  311. * */
  312. getMp3Data() {
  313. if (!recorder.fileSize) {
  314. this.$message.error("请录音后在上传语音");
  315. return;
  316. }
  317. const mp3Blob = recorder.getWAVBlob();
  318. // const mp3Blob = this.convertToMp3(recorder.getWAV());
  319. let audioFile = this.dataURLtoAudio(mp3Blob, "wav");
  320. console.log(audioFile);
  321. let iiframe = this.$refs['iiframe']
  322. // this.isloading = true
  323. // this.beforeUpload1(audioFile, 3);
  324. // return;
  325. if (this.cjson.type == 'theme' || this.cjson.type == 'QA') {
  326. this.isloading = true
  327. let _this = this
  328. let _result = ``;
  329. iiframe.contentWindow.onRecognizedResult = function (e) {
  330. console.log('onRecognizedResult', e);
  331. let privText = e.privText
  332. _result+=privText;
  333. // _this.beforeUpload1(audioFile, 3, privText);
  334. }
  335. iiframe.contentWindow.onSessionStopped = function(e){
  336. console.log("转译完成")
  337. console.log(e);
  338. _this.beforeUpload1(audioFile, 3, _result);
  339. }
  340. iiframe.contentWindow.doContinuousPronunciationAssessment('', { files: [audioFile] })
  341. } else if (this.cjson.type == 'createRole') {
  342. // this.isloading = true
  343. // this.beforeUpload1(audioFile, 3);
  344. this.isloading = true
  345. let _this = this
  346. let _result = ``;
  347. iiframe.contentWindow.onRecognizedResult = function (e) {
  348. console.log('onRecognizedResult', e);
  349. let privText = e.privText
  350. _result+=privText;
  351. // _this.beforeUpload1(audioFile, 3, privText);
  352. }
  353. iiframe.contentWindow.onSessionStopped = function(e){
  354. console.log("转译完成")
  355. console.log(e);
  356. _this.beforeUpload1(audioFile, 3, _result);
  357. }
  358. iiframe.contentWindow.doContinuousPronunciationAssessment('', { files: [audioFile] })
  359. } else {
  360. this.isloading = true
  361. let _this = this
  362. let _result = ``;
  363. let _star = ``;
  364. iiframe.contentWindow.onRecognizedResult = function (e) {
  365. console.log('onRecognizedResult', e);
  366. let privText = e.privText
  367. _result +=privText;
  368. _star = JSON.parse(e.privJson).NBest[0].PronunciationAssessment
  369. console.log(_star)
  370. // console.log('star', star)
  371. // e.privText
  372. // JSON.parse(e.privJson).NBest[0].PronunciationAssessment
  373. // _this.beforeUpload1(audioFile, 3, privText, star);
  374. }
  375. iiframe.contentWindow.onSessionStopped = function(e){
  376. console.log("转译完成")
  377. console.log(e);
  378. _this.beforeUpload1(audioFile, 3, _result, _star);
  379. }
  380. iiframe.contentWindow.doContinuousPronunciationAssessment(this.cjson.content, { files: [audioFile] })
  381. }
  382. // recorder.download(mp3Blob, "recorder", "mp3");
  383. },
  384. convertToMp3(wavDataView) {
  385. // 获取wav头信息
  386. const wav = lamejs.WavHeader.readHeader(wavDataView); // 此处其实可以不用去读wav头信息,毕竟有对应的config配置
  387. const { channels, sampleRate } = wav;
  388. const mp3enc = new lamejs.Mp3Encoder(channels, sampleRate, 128);
  389. // 获取左右通道数据
  390. const result = recorder.getChannelData();
  391. const buffer = [];
  392. const leftData =
  393. result.left &&
  394. new Int16Array(result.left.buffer, 0, result.left.byteLength / 2);
  395. const rightData =
  396. result.right &&
  397. new Int16Array(result.right.buffer, 0, result.right.byteLength / 2);
  398. const remaining = leftData.length + (rightData ? rightData.length : 0);
  399. const maxSamples = 1152;
  400. for (let i = 0; i < remaining; i += maxSamples) {
  401. const left = leftData.subarray(i, i + maxSamples);
  402. let right = null;
  403. let mp3buf = null;
  404. if (channels === 2) {
  405. right = rightData.subarray(i, i + maxSamples);
  406. mp3buf = mp3enc.encodeBuffer(left, right);
  407. } else {
  408. mp3buf = mp3enc.encodeBuffer(left);
  409. }
  410. if (mp3buf.length > 0) {
  411. buffer.push(mp3buf);
  412. }
  413. }
  414. const enc = mp3enc.flush();
  415. if (enc.length > 0) {
  416. buffer.push(enc);
  417. }
  418. return new Blob(buffer, { type: "audio/wav" });
  419. },
  420. dataURLtoAudio(blob, filename) {
  421. return new File([blob], filename, { type: "audio/wav" });
  422. },
  423. beforeUpload1(event, type, text, star) {
  424. var file;
  425. if (type == 3) {
  426. file = event;
  427. } else {
  428. file = event.target.files[0];
  429. }
  430. var credentials = {
  431. accessKeyId: "AKIATLPEDU37QV5CHLMH",
  432. secretAccessKey: "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",
  433. }; //秘钥形式的登录上传
  434. window.AWS.config.update(credentials);
  435. window.AWS.config.region = "cn-northwest-1"; //设置区域
  436. var bucket = new window.AWS.S3({ params: { Bucket: "ccrb" } }); //选择桶
  437. var _this = this;
  438. if (file) {
  439. var params = {
  440. Key:
  441. file.name.split(".")[0] +
  442. new Date().getTime() +
  443. "." +
  444. file.name.split(".")[file.name.split(".").length - 1],
  445. ContentType: file.type,
  446. Body: file,
  447. "Access-Control-Allow-Credentials": "*",
  448. ACL: "public-read",
  449. }; //key可以设置为桶的相抵路径,Body为文件, ACL最好要设置
  450. var options = {
  451. partSize: 2048 * 1024 * 1024,
  452. queueSize: 2,
  453. leavePartsOnError: true,
  454. };
  455. bucket
  456. .upload(params, options)
  457. .on("httpUploadProgress", function (evt) {
  458. //这里可以写进度条
  459. // console.log("Uploaded : " + parseInt((evt.loaded * 80) / evt.total) + '%');
  460. // _this.progress = parseInt((evt.loaded * 80) / evt.total);
  461. })
  462. .send(function (err, data) {
  463. if (_this.cjson.type != 'createRole') {
  464. _this.isloading = false
  465. }
  466. // _this.progress = 100;
  467. if (err) {
  468. var a = _this.$refs.upload1.uploadFiles;
  469. a.splice(a.length - 1, a.length);
  470. _this.$message.error("上传失败");
  471. } else {
  472. if (type == 3) {
  473. if (_this.cjson.type == 'createRole') {
  474. _this.answerArray.push(
  475. {
  476. isY: true,
  477. content: text,
  478. voice: data.Location,
  479. name: '',
  480. img: ''
  481. }
  482. )
  483. _this.answerCode(text)
  484. } else {
  485. _this.LuAudioUrl = data.Location;
  486. _this.$emit('setWork', _this.LuAudioUrl, _this.checkType, text, star)
  487. }
  488. }
  489. console.log(data.Location);
  490. }
  491. });
  492. }
  493. },
  494. calculateParentHeight() {
  495. this.$nextTick(() => {
  496. setTimeout(() => {
  497. // 获取父元素
  498. var parentElement = this.$refs['wb'];
  499. // 计算父元素的高度
  500. // var parentHeight = parentElement.offsetHeight + 1;
  501. var parentWidth = parentElement.offsetWidth;
  502. // this.oheight = parentHeight + 'px'
  503. this.oWidth = parentWidth + 'px'
  504. }, 50);
  505. });
  506. },
  507. setSecodes() {
  508. this.totalSeconds = this.checkJson[this.checkType].oTime * 60
  509. // this.totalSeconds = 10
  510. this.calcTimer = setInterval(() => {
  511. if (this.totalSeconds > 0) {
  512. this.totalSeconds--;
  513. } else {
  514. clearInterval(this.calcTimer);
  515. this.calcTimer = null
  516. this.startRecorder()
  517. console.log("倒计时结束"); // 输出日志
  518. }
  519. }, 1000);
  520. },
  521. answerCode(msg) {
  522. var _this = this;
  523. if (msg) {
  524. _this.ajax.post('https://gpt4.cocorobo.cn/assistants_completion_response', {
  525. uid: _this.id,
  526. message: msg,
  527. }).then(function (response) {
  528. console.log(response);
  529. _this.answerArray.push(
  530. {
  531. isY: false,
  532. content: response.data.FunctionResponse,
  533. name: _this.answerArray[0].name,
  534. img: _this.answerArray[0].img
  535. }
  536. )
  537. console.log(_this.answerArray);
  538. _this.$forceUpdate()
  539. _this.isloading = false
  540. _this.$emit('setWork', _this.answerArray, _this.checkType)
  541. }).catch(function (error) {
  542. _this.isloading = false
  543. console.log(error);
  544. });
  545. } else {
  546. _this.answerArray.push(
  547. {
  548. isY: false,
  549. content: "抱歉,您刚刚没有成功录入内容,请再说一遍!",
  550. name: _this.answerArray[0].name,
  551. img: _this.answerArray[0].img
  552. }
  553. )
  554. _this.isloading = false
  555. }
  556. },
  557. guid() {
  558. var _num,
  559. i,
  560. _guid = "";
  561. for (i = 0; i < 32; i++) {
  562. _guid += Math.floor(Math.random() * 16).toString(16); //随机0 - 16 的数字 转变为16进制的字符串
  563. _num = Math.floor((i - 7) / 4); //计算 (i-7)除4
  564. if (_num > -1 && _num < 4 && i == 7 + 4 * _num) {
  565. //会使guid中间加 "-" 形式为xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
  566. _guid += "-";
  567. }
  568. }
  569. return _guid;
  570. },
  571. createRole(content, name) {
  572. var _this = this;
  573. _this.ajax.post('https://gpt4.cocorobo.cn/create_free_assistants', {
  574. fileName: [],
  575. url: [],
  576. uid: _this.id,
  577. instructions: content,
  578. assistantName: name
  579. }).then(function (response) {
  580. console.log(response);
  581. }).catch(function (error) {
  582. console.log(error);
  583. });
  584. }
  585. },
  586. beforeDestroy() {
  587. if (!this.isRecord) {
  588. } else {
  589. if (this.calcTimer) {
  590. clearInterval(this.calcTimer)
  591. this.calcTimer = null;
  592. }
  593. recorder.stop(); // 结束录音
  594. }
  595. },
  596. mounted() {
  597. this.cjson = JSON.parse(JSON.stringify(this.checkJson[this.checkType]));
  598. this.LuAudioUrl = ''
  599. if (typeof this.work[this.checkType] == 'string') {
  600. this.LuAudioUrl = this.work[this.checkType] ? JSON.parse(JSON.stringify(this.work[this.checkType])) : ''
  601. } else if (typeof this.work[this.checkType] == 'object' && this.cjson.type != 'createRole') {
  602. this.LuAudioUrl = this.work[this.checkType].audio ? JSON.parse(JSON.stringify(this.work[this.checkType].audio)) : ''
  603. } else if (typeof this.work[this.checkType] == 'object' && this.cjson.type == 'createRole') {
  604. var a = Array.isArray(this.work[this.checkType])
  605. if (a) {
  606. this.answerArray = JSON.parse(JSON.stringify(this.work[this.checkType]))
  607. } else {
  608. this.answerArray = []
  609. this.answerArray.push(
  610. {
  611. isY: false,
  612. content: this.cjson.content3,
  613. name: this.cjson.content,
  614. img: this.cjson.img
  615. }
  616. )
  617. }
  618. this.$emit('setWork', this.answerArray, this.checkType)
  619. }
  620. if (!this.work[this.checkType] && this.cjson.type == 'createRole') {
  621. var a = Array.isArray(this.work[this.checkType])
  622. if (a) {
  623. this.answerArray = JSON.parse(JSON.stringify(this.work[this.checkType]))
  624. } else {
  625. this.answerArray = []
  626. this.answerArray.push(
  627. {
  628. isY: false,
  629. content: this.cjson.content3,
  630. name: this.cjson.content,
  631. img: this.cjson.img
  632. }
  633. )
  634. }
  635. this.$emit('setWork', this.answerArray, this.checkType)
  636. }
  637. this.star = this.work[this.checkType] ? (this.work[this.checkType].score ? JSON.parse(JSON.stringify(this.work[this.checkType].score)) : 0) : 0
  638. if (this.cjson.type != "createRole") {
  639. this.calculateParentHeight()
  640. }
  641. if (this.cjson.type == "createRole") {
  642. this.createRole(this.cjson.content2, this.cjson.content)
  643. }
  644. },
  645. }
  646. </script>
  647. <style scoped>
  648. .o_box {
  649. width: 100%;
  650. height: 100%;
  651. background-image: url('../../../assets/icon/env_background.png');
  652. background-size: cover;
  653. }
  654. .o_top {
  655. height: 65px;
  656. position: absolute;
  657. }
  658. .o_content {
  659. height: calc(100% - 210px);
  660. display: flex;
  661. align-items: center;
  662. justify-content: center;
  663. overflow: hidden;
  664. flex-direction: column;
  665. }
  666. .o_bottom {
  667. height: 210px;
  668. display: flex;
  669. flex-direction: column;
  670. align-items: center;
  671. justify-content: center;
  672. }
  673. .word_box {
  674. min-width: 500px;
  675. max-width: 70%;
  676. background: #fff;
  677. border-radius: 10px;
  678. position: relative;
  679. /* max-height: calc(100% - 40px); */
  680. max-height: calc(100% - 70px);
  681. /* overflow: auto; */
  682. }
  683. .tips_box {
  684. margin-top: 30px;
  685. color: #727272;
  686. }
  687. .word_box>.word_bbox {
  688. width: 100%;
  689. position: relative;
  690. z-index: 999;
  691. max-height: 100%;
  692. overflow: auto;
  693. }
  694. .sentence_box {
  695. background: #e0e0e04d;
  696. min-width: 500px;
  697. max-width: 70%;
  698. border-radius: 15px;
  699. /* max-height: 100%; */
  700. overflow: auto;
  701. padding: 15px;
  702. font-size: 16px;
  703. color: #000;
  704. line-height: 20px;
  705. word-break: break-word;
  706. white-space: pre-line;
  707. max-height: calc(100% - 80px);
  708. }
  709. .word_box::before {
  710. content: '';
  711. position: absolute;
  712. width: 100%;
  713. height: 100%;
  714. display: block;
  715. box-shadow: 0 0 4px 4px #1d39830d;
  716. border-radius: 10px;
  717. z-index: 2;
  718. background: #fff;
  719. }
  720. .word_box::after {
  721. content: '';
  722. position: absolute;
  723. width: 100%;
  724. height: 100%;
  725. background: #fff;
  726. display: block;
  727. box-shadow: 0 0 4px 4px #1d39830d;
  728. border-radius: 10px;
  729. z-index: 1;
  730. top: 15px;
  731. left: 15px;
  732. }
  733. .word_box>.word_bbox>.word_img {
  734. width: calc(100% - 30px);
  735. max-height: 300px;
  736. z-index: 999;
  737. position: relative;
  738. margin: 15px auto;
  739. display: block;
  740. border-radius: 10px;
  741. cursor: pointer;
  742. object-fit: contain;
  743. }
  744. .word_box>.word_bbox>.word_content {
  745. position: relative;
  746. z-index: 999;
  747. text-align: center;
  748. font-size: 36px;
  749. margin: 15px;
  750. font-weight: bold;
  751. color: #000;
  752. width: calc(100% - 30px);
  753. word-break: break-word;
  754. white-space: pre-line;
  755. }
  756. .word_box>.word_bbox>.word_content2 {
  757. position: relative;
  758. z-index: 999;
  759. text-align: left;
  760. font-size: 16px;
  761. margin: 15px;
  762. color: #727272;
  763. width: calc(100% - 30px);
  764. word-break: break-word;
  765. white-space: pre-line;
  766. /* margin-top: 10px; */
  767. }
  768. .o_bottom .audio {
  769. display: flex;
  770. align-items: center;
  771. justify-content: center;
  772. }
  773. .o_bottom .audio>img {
  774. width: 75px;
  775. height: 75px;
  776. cursor: pointer;
  777. }
  778. .o_bottom .audio_word {
  779. display: flex;
  780. align-items: center;
  781. justify-content: center;
  782. color: #00000099;
  783. font-size: 16px;
  784. margin: 10px 0 8px;
  785. }
  786. .o_bottom .audio_index {
  787. display: flex;
  788. align-items: center;
  789. justify-content: center;
  790. }
  791. .audio_index_last,
  792. .audio_index_next {
  793. height: 40px;
  794. width: 40px;
  795. background: #3681fc;
  796. border-radius: 50%;
  797. display: flex;
  798. align-items: center;
  799. justify-content: center;
  800. cursor: pointer;
  801. }
  802. .audio_index_last>img,
  803. .audio_index_next>img {
  804. width: 15px;
  805. height: auto;
  806. }
  807. .audio_index_last.disabled,
  808. .audio_index_next.disabled {
  809. opacity: .6;
  810. }
  811. .audio_index_last>img {
  812. transform: rotate(180deg);
  813. }
  814. .audio_index_last {
  815. margin-right: 20px;
  816. }
  817. .audio_index_content {
  818. color: #000;
  819. font-size: 16px;
  820. }
  821. .audio_index_next {
  822. margin-left: 20px;
  823. }
  824. .audio_ing {
  825. color: #EE3E3E;
  826. margin-top: 25px;
  827. font-size: 12px;
  828. display: flex;
  829. align-items: center;
  830. justify-content: center;
  831. }
  832. .audio_b {
  833. display: flex;
  834. align-items: center;
  835. justify-content: center;
  836. margin-bottom: 15px;
  837. }
  838. .audio_rerecord {
  839. display: flex;
  840. align-items: center;
  841. justify-content: center;
  842. margin-bottom: 15px;
  843. }
  844. .audio_rerecord>span {
  845. display: flex;
  846. border: 1px solid #3981FA;
  847. align-items: center;
  848. color: #3981FA;
  849. padding: 5px 10px;
  850. border-radius: 5px;
  851. cursor: pointer;
  852. }
  853. .audio_rerecord>span::before {
  854. content: '';
  855. width: 15px;
  856. height: 15px;
  857. background: url('../../../assets/icon/englishVoice/restart.png');
  858. display: block;
  859. background-size: 100% 100%;
  860. margin-right: 5px;
  861. }
  862. .audio_class {
  863. background: #3680fb !important;
  864. margin: 0 !important;
  865. }
  866. .audio_b>>>.vueAudioBetter span:before {
  867. color: #fff;
  868. }
  869. .audio_class>>>.slider .process {
  870. background: #000;
  871. }
  872. .audio_b>>>.vueAudioBetter .iconfont:active {
  873. position: unset !important;
  874. }
  875. .time_box {
  876. display: flex;
  877. flex-direction: column;
  878. align-items: center;
  879. justify-content: center;
  880. margin-top: 25px;
  881. }
  882. .time_box>span:nth-child(1) {
  883. color: #727272;
  884. font-size: 16px;
  885. }
  886. .time_box>span:nth-child(2) {
  887. /* margin-top: 10px; */
  888. font-size: 32px;
  889. color: #3581FC;
  890. }
  891. .type_box {
  892. min-width: 500px;
  893. width: 70%;
  894. text-align: right;
  895. color: #a5a5a5;
  896. margin-bottom: 10px;
  897. }
  898. .sentence_div {
  899. width: 100%;
  900. overflow: hidden;
  901. margin-top: 10px;
  902. display: flex;
  903. justify-content: flex-end;
  904. }
  905. .sentence_div>img {
  906. width: 60px;
  907. height: 60px;
  908. object-fit: cover;
  909. cursor: pointer;
  910. border-radius: 4px
  911. }
  912. .star_box {
  913. display: flex;
  914. align-items: center;
  915. margin-bottom: 10px
  916. }
  917. .star_box>.star {
  918. width: 25px;
  919. height: 25px;
  920. display: block;
  921. background-image: url('../../../assets/icon/englishVoice/star-no.png');
  922. background-size: 100% 100%;
  923. }
  924. .star_box>.star+.star {
  925. margin-left: 5px;
  926. }
  927. .star_box>.starA {
  928. background-image: url('../../../assets/icon/englishVoice/star.png');
  929. }
  930. </style>