|
@@ -0,0 +1,495 @@
|
|
|
+<template>
|
|
|
+ <el-dialog title="对话测试" width="400px" :visible.sync="dataDialog" :append-to-body="true" :before-close="handleClose"
|
|
|
+ class="dialog_diy">
|
|
|
+ <div v-loading="isloading">
|
|
|
+ <div class="dialog_content">
|
|
|
+ <div class="dialog" v-for="(item, index) in answerArray" :key="index" :class="{ dialog_right: item.isY }">
|
|
|
+ <div class="d_img">
|
|
|
+ <img :src="item.img ? item.img : require('../../../../../assets/icon/englishVoice/icon_portal.png')"
|
|
|
+ alt="">
|
|
|
+ </div>
|
|
|
+ <div class="d_content">
|
|
|
+ <div class="d_name" v-if="item.name">{{ item.name }}</div>
|
|
|
+ <div class="d_voice" v-if="item.voice">
|
|
|
+ <mini-audio :audio-source="item.voice" class="audio_class"></mini-audio>
|
|
|
+ </div>
|
|
|
+ <div class="d_log" v-if="item.content">{{ item.content }}</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="dialog_answer">
|
|
|
+ <span v-if="isRecord">正在录音中,再次点击话筒停止录音...</span>
|
|
|
+ <img @click="startRecorder" src="../../../../../assets/icon/englishVoice/icon_voice.png" alt="">
|
|
|
+ </div>
|
|
|
+ <iframe allow="camera *; microphone *;display-capture;midi;encrypted-media;"
|
|
|
+ src="https://beta.cloud.cocorobo.cn/browser/public/index.html" ref="iiframe"></iframe>
|
|
|
+ </div>
|
|
|
+ </el-dialog>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+import Recorder from "js-audio-recorder";
|
|
|
+const lamejs = require("lamejs");
|
|
|
+
|
|
|
+const recorder = new Recorder({
|
|
|
+ sampleBits: 16, // 采样位数,支持 8 或 16,默认是16
|
|
|
+ sampleRate: 48000, // 采样率,支持 11025、16000、22050、24000、44100、48000,根据浏览器默认值,我的chrome是48000
|
|
|
+ numChannels: 1 // 声道,支持 1 或 2, 默认是1
|
|
|
+ // compiling: false,(0.x版本中生效,1.x增加中) // 是否边录边转换,默认是false
|
|
|
+});
|
|
|
+
|
|
|
+// 绑定事件-打印的是当前录音数据
|
|
|
+recorder.onprogress = function (params) {
|
|
|
+ // console.log('--------------START---------------')
|
|
|
+ // console.log('录音时长(秒)', params.duration);
|
|
|
+ // console.log('录音大小(字节)', params.fileSize);
|
|
|
+ // console.log('录音音量百分比(%)', params.vol);
|
|
|
+ // console.log('当前录音的总数据([DataView, DataView...])', params.data);
|
|
|
+ // console.log('--------------END---------------')
|
|
|
+};
|
|
|
+export default {
|
|
|
+ components: {
|
|
|
+ },
|
|
|
+ props: {
|
|
|
+ dataDialog: {
|
|
|
+ type: Boolean,
|
|
|
+ default: false
|
|
|
+ },
|
|
|
+ checkJson: {
|
|
|
+ type: Object,
|
|
|
+ }
|
|
|
+ },
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ json: [],
|
|
|
+ answerArray: [],
|
|
|
+ isRecord: false,
|
|
|
+ isPlayerRecord: false,
|
|
|
+ isloading: false,
|
|
|
+ };
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ handleClose(done) {
|
|
|
+ this.close();
|
|
|
+ done();
|
|
|
+ },
|
|
|
+ close() {
|
|
|
+ this.$emit("update:dataDialog", false);
|
|
|
+ },
|
|
|
+ close2() {
|
|
|
+ this.$emit("update:dataDialog", false);
|
|
|
+ },
|
|
|
+ confirm() {
|
|
|
+ this.close2();
|
|
|
+ },
|
|
|
+ setVoiceJson(val) {
|
|
|
+ let a = JSON.parse(JSON.stringify(val));
|
|
|
+ this.json = a;
|
|
|
+ this.answerArray = []
|
|
|
+ this.answerArray.push(
|
|
|
+ {
|
|
|
+ isY: false,
|
|
|
+ content: a.content3,
|
|
|
+ name: a.content,
|
|
|
+ img: a.img
|
|
|
+ }
|
|
|
+ )
|
|
|
+ // let iiframe = this.$refs['iiframe']
|
|
|
+ // .doPronunciationAssessmentOnceAsync()
|
|
|
+ // console.log(iiframe);
|
|
|
+ // this.answerArray.push(
|
|
|
+ // {
|
|
|
+ // isY: true,
|
|
|
+ // content: a.content3,
|
|
|
+ // name: a.content,
|
|
|
+ // }
|
|
|
+ // )
|
|
|
+ this.createRole(a.content2, a.content)
|
|
|
+ },
|
|
|
+ answerCode(msg) {
|
|
|
+ var _this = this;
|
|
|
+ _this.ajax.post('https://gpt4.cocorobo.cn/assistants_completion_response', {
|
|
|
+ uid: _this.guid(),
|
|
|
+ message: msg,
|
|
|
+ }).then(function (response) {
|
|
|
+ console.log(response);
|
|
|
+ _this.answerArray.push(
|
|
|
+ {
|
|
|
+ isY: false,
|
|
|
+ content: response.FunctionResponse,
|
|
|
+ name: _this.answerArray[0].name,
|
|
|
+ img: _this.answerArray[0].img
|
|
|
+ }
|
|
|
+ )
|
|
|
+ _this.isloading = false
|
|
|
+ }).catch(function (error) {
|
|
|
+ _this.isloading = false
|
|
|
+ console.log(error);
|
|
|
+ });
|
|
|
+ },
|
|
|
+ // 开始录音
|
|
|
+ startRecorder() {
|
|
|
+ let _this = this;
|
|
|
+ if (!_this.isRecord) {
|
|
|
+ recorder.destroy(); // 销毁录音
|
|
|
+ _this.isRecord = true;
|
|
|
+ recorder.start().then(
|
|
|
+ () => { },
|
|
|
+ (error) => {
|
|
|
+ _this.isRecord = false;
|
|
|
+ // _this.$message.error(`${error.name} : ${error.message}`);
|
|
|
+ _this.$message.error(`没有找到可使用的麦克风,或者您没有允许此网页使用麦克风`);
|
|
|
+ // 出错了
|
|
|
+ console.log(`${error.name} : ${error.message}`);
|
|
|
+ }
|
|
|
+ );
|
|
|
+
|
|
|
+ } else {
|
|
|
+ _this.isRecord = false;
|
|
|
+ recorder.stop(); // 结束录音
|
|
|
+ this.getMp3Data()
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 录音播放
|
|
|
+ playRecorder() {
|
|
|
+ if (!recorder.fileSize) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (!this.isPlayerRecord) {
|
|
|
+ this.isPlayerRecord = true;
|
|
|
+ recorder.play();
|
|
|
+ } else {
|
|
|
+ this.isPlayerRecord = false;
|
|
|
+ recorder.stopPlay(); // 停止录音播放
|
|
|
+ }
|
|
|
+ recorder.onplayend = () => {
|
|
|
+ this.isPlayerRecord = false;
|
|
|
+ console.log("onplayend");
|
|
|
+ };
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 文件格式转换 wav-map3
|
|
|
+ * */
|
|
|
+ getMp3Data() {
|
|
|
+ if (!recorder.fileSize) {
|
|
|
+ this.$message.error("请录音后在上传语音");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const mp3Blob = this.convertToMp3(recorder.getWAV());
|
|
|
+ let audioFile = this.dataURLtoAudio(mp3Blob, "mp3");
|
|
|
+ console.log(audioFile);
|
|
|
+ let iiframe = this.$refs['iiframe']
|
|
|
+ iiframe.contentWindow.doPronunciationAssessmentOnceAsync('', { files: [audioFile] })
|
|
|
+ this.isloading = true
|
|
|
+ let _this = this
|
|
|
+ iiframe.contentWindow.onRecognizedResult = function (e) {
|
|
|
+ console.log('onRecognizedResult', e);
|
|
|
+ let privText = e.privText
|
|
|
+ // e.privText
|
|
|
+ // JSON.parse(e.privJson).NBest[0].PronunciationAssessment
|
|
|
+
|
|
|
+ _this.beforeUpload1(audioFile, 3, privText);
|
|
|
+ }
|
|
|
+ // recorder.download(mp3Blob, "recorder", "mp3");
|
|
|
+ },
|
|
|
+ convertToMp3(wavDataView) {
|
|
|
+ // 获取wav头信息
|
|
|
+ const wav = lamejs.WavHeader.readHeader(wavDataView); // 此处其实可以不用去读wav头信息,毕竟有对应的config配置
|
|
|
+ const { channels, sampleRate } = wav;
|
|
|
+ const mp3enc = new lamejs.Mp3Encoder(channels, sampleRate, 128);
|
|
|
+ // 获取左右通道数据
|
|
|
+ const result = recorder.getChannelData();
|
|
|
+ const buffer = [];
|
|
|
+ const leftData =
|
|
|
+ result.left &&
|
|
|
+ new Int16Array(result.left.buffer, 0, result.left.byteLength / 2);
|
|
|
+ const rightData =
|
|
|
+ result.right &&
|
|
|
+ new Int16Array(result.right.buffer, 0, result.right.byteLength / 2);
|
|
|
+ const remaining = leftData.length + (rightData ? rightData.length : 0);
|
|
|
+ const maxSamples = 1152;
|
|
|
+ for (let i = 0; i < remaining; i += maxSamples) {
|
|
|
+ const left = leftData.subarray(i, i + maxSamples);
|
|
|
+ let right = null;
|
|
|
+ let mp3buf = null;
|
|
|
+ if (channels === 2) {
|
|
|
+ right = rightData.subarray(i, i + maxSamples);
|
|
|
+ mp3buf = mp3enc.encodeBuffer(left, right);
|
|
|
+ } else {
|
|
|
+ mp3buf = mp3enc.encodeBuffer(left);
|
|
|
+ }
|
|
|
+ if (mp3buf.length > 0) {
|
|
|
+ buffer.push(mp3buf);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ const enc = mp3enc.flush();
|
|
|
+ if (enc.length > 0) {
|
|
|
+ buffer.push(enc);
|
|
|
+ }
|
|
|
+ return new Blob(buffer, { type: "audio/mp3" });
|
|
|
+ },
|
|
|
+ dataURLtoAudio(blob, filename) {
|
|
|
+ return new File([blob], filename, { type: "audio/mp3" });
|
|
|
+ },
|
|
|
+ beforeUpload1(event, type, privText) {
|
|
|
+ var file;
|
|
|
+ if (type == 3) {
|
|
|
+ file = event;
|
|
|
+ } else {
|
|
|
+ file = event.target.files[0];
|
|
|
+ }
|
|
|
+ var credentials = {
|
|
|
+ accessKeyId: "AKIATLPEDU37QV5CHLMH",
|
|
|
+ secretAccessKey: "Q2SQw37HfolS7yeaR1Ndpy9Jl4E2YZKUuuy2muZR",
|
|
|
+ }; //秘钥形式的登录上传
|
|
|
+ window.AWS.config.update(credentials);
|
|
|
+ window.AWS.config.region = "cn-northwest-1"; //设置区域
|
|
|
+
|
|
|
+ var bucket = new window.AWS.S3({ params: { Bucket: "ccrb" } }); //选择桶
|
|
|
+ var _this = this;
|
|
|
+
|
|
|
+ if (file) {
|
|
|
+ var params = {
|
|
|
+ Key:
|
|
|
+ file.name.split(".")[0] +
|
|
|
+ new Date().getTime() +
|
|
|
+ "." +
|
|
|
+ file.name.split(".")[file.name.split(".").length - 1],
|
|
|
+ ContentType: file.type,
|
|
|
+ Body: file,
|
|
|
+ "Access-Control-Allow-Credentials": "*",
|
|
|
+ ACL: "public-read",
|
|
|
+ }; //key可以设置为桶的相抵路径,Body为文件, ACL最好要设置
|
|
|
+ var options = {
|
|
|
+ partSize: 2048 * 1024 * 1024,
|
|
|
+ queueSize: 2,
|
|
|
+ leavePartsOnError: true,
|
|
|
+ };
|
|
|
+ bucket
|
|
|
+ .upload(params, options)
|
|
|
+ .on("httpUploadProgress", function (evt) {
|
|
|
+ //这里可以写进度条
|
|
|
+ // console.log("Uploaded : " + parseInt((evt.loaded * 80) / evt.total) + '%');
|
|
|
+ // _this.progress = parseInt((evt.loaded * 80) / evt.total);
|
|
|
+ })
|
|
|
+ .send(function (err, data) {
|
|
|
+ // _this.progress = 100;
|
|
|
+ if (err) {
|
|
|
+ // var a = _this.$refs.upload1.uploadFiles;
|
|
|
+ // a.splice(a.length - 1, a.length);
|
|
|
+ _this.$message.error("上传失败");
|
|
|
+ } else {
|
|
|
+ if (type == 3) {
|
|
|
+ // _this.LuAudioUrl = data.Location;
|
|
|
+ _this.answerArray.push(
|
|
|
+ {
|
|
|
+ isY: true,
|
|
|
+ content: privText,
|
|
|
+ voice: data.Location,
|
|
|
+ name: '',
|
|
|
+ img: ''
|
|
|
+ }
|
|
|
+ )
|
|
|
+ _this.answerCode(privText)
|
|
|
+ }
|
|
|
+ console.log(data.Location);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ },
|
|
|
+ guid() {
|
|
|
+ var _num,
|
|
|
+ i,
|
|
|
+ _guid = "";
|
|
|
+ for (i = 0; i < 32; i++) {
|
|
|
+ _guid += Math.floor(Math.random() * 16).toString(16); //随机0 - 16 的数字 转变为16进制的字符串
|
|
|
+ _num = Math.floor((i - 7) / 4); //计算 (i-7)除4
|
|
|
+ if (_num > -1 && _num < 4 && i == 7 + 4 * _num) {
|
|
|
+ //会使guid中间加 "-" 形式为xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
|
|
+ _guid += "-";
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return _guid;
|
|
|
+ },
|
|
|
+ createRole(content, name) {
|
|
|
+ var _this = this;
|
|
|
+ _this.ajax.post('https://gpt4.cocorobo.cn/create_free_assistants', {
|
|
|
+ filename: [],
|
|
|
+ url: [],
|
|
|
+ uid: _this.guid(),
|
|
|
+ instructions: content,
|
|
|
+ assistantName: name
|
|
|
+ }).then(function (response) {
|
|
|
+ console.log(response);
|
|
|
+ }).catch(function (error) {
|
|
|
+ console.log(error);
|
|
|
+ });
|
|
|
+ }
|
|
|
+ },
|
|
|
+ watch: {
|
|
|
+ dataDialog: {
|
|
|
+ handler: function (newVal, oldVal) {
|
|
|
+ if (newVal) {
|
|
|
+ this.setVoiceJson(this.checkJson);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ deep: true
|
|
|
+ }
|
|
|
+ },
|
|
|
+ mounted() {
|
|
|
+ this.setVoiceJson(this.checkJson);
|
|
|
+ }
|
|
|
+};
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.dialog_diy>>>.el-dialog {
|
|
|
+ /* width: 100%; */
|
|
|
+ /* max-width: 1000px; */
|
|
|
+ /* height: 100%; */
|
|
|
+ /* margin: 0vh auto !important; */
|
|
|
+}
|
|
|
+
|
|
|
+.dialog_diy>>>.el-dialog__header {
|
|
|
+ background: #454545 !important;
|
|
|
+ padding: 15px;
|
|
|
+}
|
|
|
+
|
|
|
+.dialog_diy>>>.el-dialog__body {
|
|
|
+ /* height: calc(100% - 54px); */
|
|
|
+ box-sizing: border-box;
|
|
|
+ padding: 15px;
|
|
|
+}
|
|
|
+
|
|
|
+.dialog_diy>>>.el-dialog__title {
|
|
|
+ color: #fff;
|
|
|
+}
|
|
|
+
|
|
|
+.dialog_diy>>>.el-dialog__headerbtn {
|
|
|
+ top: 19px;
|
|
|
+}
|
|
|
+
|
|
|
+.dialog_diy>>>.el-dialog__headerbtn .el-dialog__close {
|
|
|
+ color: #fff;
|
|
|
+}
|
|
|
+
|
|
|
+.dialog_diy>>>.el-dialog__headerbtn .el-dialog__close:hover {
|
|
|
+ color: #fff;
|
|
|
+}
|
|
|
+
|
|
|
+.dialog_diy>>>.el-dialog__body,
|
|
|
+.dialog_diy>>>.el-dialog__footer {
|
|
|
+ background: #f0f4fa;
|
|
|
+ overflow: hidden;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+.dialog_content {
|
|
|
+ height: 500px;
|
|
|
+ background: #fff;
|
|
|
+ width: 100%;
|
|
|
+ padding: 10px;
|
|
|
+ box-sizing: border-box;
|
|
|
+ border-radius: 4px;
|
|
|
+ overflow: auto;
|
|
|
+}
|
|
|
+
|
|
|
+.dialog_answer {
|
|
|
+ height: 45px;
|
|
|
+ background: #fff;
|
|
|
+ width: 100%;
|
|
|
+ padding: 10px;
|
|
|
+ box-sizing: border-box;
|
|
|
+ margin-top: 10px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ border-radius: 4px;
|
|
|
+}
|
|
|
+
|
|
|
+.dialog_answer>img {
|
|
|
+ height: 100%;
|
|
|
+ margin-left: auto;
|
|
|
+ cursor: pointer;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+.dialog {
|
|
|
+ display: flex;
|
|
|
+}
|
|
|
+
|
|
|
+.dialog+.dialog {
|
|
|
+ margin-top: 15px;
|
|
|
+}
|
|
|
+
|
|
|
+.dialog_right {
|
|
|
+ flex-direction: row-reverse;
|
|
|
+}
|
|
|
+
|
|
|
+.dialog>.d_img {
|
|
|
+ width: 35px;
|
|
|
+ height: 35px;
|
|
|
+ min-width: 35px;
|
|
|
+ overflow: hidden;
|
|
|
+ border-radius: 50%;
|
|
|
+ margin-right: 5px;
|
|
|
+}
|
|
|
+
|
|
|
+.dialog>.d_img>img {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ object-fit: cover;
|
|
|
+}
|
|
|
+
|
|
|
+.dialog_right>.d_img {
|
|
|
+ margin-right: 0;
|
|
|
+ margin-left: 5px;
|
|
|
+}
|
|
|
+
|
|
|
+.dialog>.d_content {
|
|
|
+ width: 100%;
|
|
|
+}
|
|
|
+
|
|
|
+.dialog>.d_content>.d_name {
|
|
|
+ color: #7C7C7C;
|
|
|
+ font-size: 12px;
|
|
|
+ margin-bottom: 5px;
|
|
|
+}
|
|
|
+
|
|
|
+.dialog_right>.d_content>.d_name {
|
|
|
+ text-align: right;
|
|
|
+}
|
|
|
+
|
|
|
+.dialog>.d_content>.d_log {
|
|
|
+ color: #000;
|
|
|
+ font-size: 14px;
|
|
|
+ padding: 5px;
|
|
|
+ background: #d9e7fe;
|
|
|
+}
|
|
|
+
|
|
|
+.dialog>.d_content>.d_voice {
|
|
|
+ width: 100%;
|
|
|
+}
|
|
|
+
|
|
|
+.audio_class {
|
|
|
+ background: #3680fb !important;
|
|
|
+ margin: 0 !important;
|
|
|
+ width: 100% !important;
|
|
|
+ box-sizing: border-box !important;
|
|
|
+}
|
|
|
+
|
|
|
+.audio_b>>>.vueAudioBetter span:before {
|
|
|
+ color: #fff;
|
|
|
+}
|
|
|
+
|
|
|
+.audio_class>>>.slider .process {
|
|
|
+ background: #000;
|
|
|
+}
|
|
|
+
|
|
|
+.audio_b>>>.vueAudioBetter .iconfont:active {
|
|
|
+ position: unset !important;
|
|
|
+}
|
|
|
+</style>
|
|
|
+
|