right.vue 26 KB

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