right.vue 26 KB

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