|
@@ -0,0 +1,553 @@
|
|
|
+<!-- -->
|
|
|
+<template>
|
|
|
+ <div class="NNPE-Book-record">
|
|
|
+ <div
|
|
|
+ :class="['record-main', microphoneStatus ? 'active' : '']"
|
|
|
+ >
|
|
|
+ <div class="record-main-inner" @click="microphone">
|
|
|
+ <div class="record" />
|
|
|
+ <span v-if="microphoneStatus" class="record-time">{{
|
|
|
+ handleDateTime(recordtime)
|
|
|
+ }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div
|
|
|
+ v-if="
|
|
|
+ !microphoneStatus && recordList.length > 0
|
|
|
+ "
|
|
|
+ :class="[
|
|
|
+ 'hasRecord',
|
|
|
+ bgIndex == 1 ? 'hasRecord-white' : '',
|
|
|
+ ]"
|
|
|
+ >
|
|
|
+ <div
|
|
|
+ :class="[bgIndex == 1 ? 'playBack-white' : 'playBack', hasMicro]"
|
|
|
+ @click="
|
|
|
+ playmicrophone(
|
|
|
+ selectIndex || selectIndex == 0
|
|
|
+ ? recordList[selectIndex].toltime
|
|
|
+ : ''
|
|
|
+ )
|
|
|
+ "
|
|
|
+ >
|
|
|
+ <svg-icon icon-class="play-fill" className="icon-svg"></svg-icon>
|
|
|
+ </div>
|
|
|
+ <span
|
|
|
+ class="record-time"
|
|
|
+ >{{ isPlaying ? "-" : "" }}{{ handleDateTime(recordtime) }}</span
|
|
|
+ >
|
|
|
+ <el-select
|
|
|
+ v-model="selectIndex"
|
|
|
+ placeholder="No recording"
|
|
|
+ no-data-text="No recording"
|
|
|
+ :popper-append-to-body="false"
|
|
|
+ @change="handleChangeRecord"
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="(item, index) in recordList"
|
|
|
+ :key="'recordList' + index"
|
|
|
+ :label="item.name"
|
|
|
+ :value="index"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ <a
|
|
|
+ class="record-delete"
|
|
|
+ @click="handleDelete"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+import Recorder from "js-audio-recorder"; // 录音插件
|
|
|
+export default {
|
|
|
+ components: {},
|
|
|
+ props: [
|
|
|
+ "wavData",
|
|
|
+ "type",
|
|
|
+ "fileName",
|
|
|
+ "selectData",
|
|
|
+ "bgIndex",
|
|
|
+ "tmIndex",
|
|
|
+ "answerRecordList",
|
|
|
+ ],
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ recorder: new Recorder({
|
|
|
+ sampleBits: 16, // 采样位数,支持 8 或 16,默认是16
|
|
|
+ sampleRate: 16000, // 采样率,支持 11025、16000、22050、24000、44100、48000,根据浏览器默认值,我的chrome是48000
|
|
|
+ numChannels: 1, // 声道,支持 1 或 2, 默认是1
|
|
|
+ }),
|
|
|
+ microphoneStatus: false,
|
|
|
+ hasMicro: "", // 录音后的样式class
|
|
|
+ wavblob: null,
|
|
|
+ audio: new window.Audio(),
|
|
|
+ recordList: [], // 录音文件数组
|
|
|
+ recordtime: 0, // 录音时长
|
|
|
+ timer: null, // 计时器
|
|
|
+ recordFile: 1, // 录音文件名
|
|
|
+ selectIndex: null, // 选中的录音索引
|
|
|
+ oldIndex: null, // 存储播放录音索引
|
|
|
+ playtime: 0, // 播放时间
|
|
|
+ isPlaying: false,
|
|
|
+ };
|
|
|
+ },
|
|
|
+ watch: {},
|
|
|
+ // 生命周期 - 创建完成(可以访问当前this实例)
|
|
|
+ created() {
|
|
|
+ this.handleActive();
|
|
|
+ let that = this;
|
|
|
+ window.stopAudioSound = function () {
|
|
|
+ if (that.audio) {
|
|
|
+ that.audio.pause();
|
|
|
+ }
|
|
|
+ };
|
|
|
+ },
|
|
|
+ // 生命周期 - 挂载完成(可以访问DOM元素)
|
|
|
+ mounted() {
|
|
|
+ let _this = this;
|
|
|
+ _this.recordList = _this.answerRecordList
|
|
|
+ ? JSON.parse(JSON.stringify(_this.answerRecordList))
|
|
|
+ : [];
|
|
|
+ if (_this.recordList.length > 0) {
|
|
|
+ _this.selectIndex = 0;
|
|
|
+ this.$emit("getSelectData", this.recordList[0].selectData);
|
|
|
+ _this.recordFile = _this.recordList.length + 1;
|
|
|
+ _this.handleChangeRecord(0);
|
|
|
+ _this.$emit(
|
|
|
+ "getRerordStatus",
|
|
|
+ !_this.microphoneStatus && _this.recordList.length > 0
|
|
|
+ );
|
|
|
+ _this.$forceUpdate();
|
|
|
+ }
|
|
|
+ _this.audio.addEventListener("play", function () {
|
|
|
+ _this.changeStatus("active");
|
|
|
+ _this.isPlaying = true;
|
|
|
+ _this.$emit("getPlayStatus", true);
|
|
|
+ });
|
|
|
+ _this.audio.addEventListener("pause", function () {
|
|
|
+ _this.changeStatus("normal");
|
|
|
+ _this.$emit("getPlayStatus", false);
|
|
|
+ });
|
|
|
+ _this.audio.addEventListener("ended", function () {
|
|
|
+ _this.changeStatus("normal");
|
|
|
+ _this.isPlaying = false;
|
|
|
+ _this.$emit("getPlayStatus", false);
|
|
|
+ });
|
|
|
+ },
|
|
|
+ beforeCreate() {}, // 生命周期 - 创建之前
|
|
|
+ beforeMount() {}, // 生命周期 - 挂载之前
|
|
|
+ beforeUpdate() {}, // 生命周期 - 更新之前
|
|
|
+ updated() {}, // 生命周期 - 更新之后
|
|
|
+ beforeDestroy() {}, // 生命周期 - 销毁之前
|
|
|
+ destroyed() {}, // 生命周期 - 销毁完成
|
|
|
+ activated() {},
|
|
|
+ // 方法集合
|
|
|
+ methods: {
|
|
|
+ // 开始录音
|
|
|
+ microphone() {
|
|
|
+ let _this = this;
|
|
|
+ if (!this.microphoneStatus) {
|
|
|
+ _this.hasMicro = "";
|
|
|
+ _this.$emit("getWavblob", null);
|
|
|
+ this.$emit("getSelectData", { type: "" });
|
|
|
+ // 开始录音
|
|
|
+ this.recorder.start();
|
|
|
+ this.microphoneStatus = true;
|
|
|
+ this.recordtime = 0;
|
|
|
+ this.isPlaying = false;
|
|
|
+ clearInterval(_this.timer);
|
|
|
+ _this.timer = setInterval(() => {
|
|
|
+ _this.recordtime++;
|
|
|
+ }, 1000);
|
|
|
+ this.$emit("handleParentPlay");
|
|
|
+ let obj = {
|
|
|
+ name: _this.fileName
|
|
|
+ ? _this.fileName + _this.recordFile
|
|
|
+ : "Recording " + _this.recordFile,
|
|
|
+ id: _this.recordFile + Math.round(Math.random() * 10),
|
|
|
+ };
|
|
|
+ console.log(this.selectData);
|
|
|
+ if (this.selectData) obj.selectData = this.selectData;
|
|
|
+ _this.recordList.push(obj);
|
|
|
+ _this.recordFile++;
|
|
|
+ _this.selectIndex = _this.recordList.length - 1;
|
|
|
+ } else {
|
|
|
+ this.hasMicro = "normal";
|
|
|
+ this.recorder.stop();
|
|
|
+ clearInterval(_this.timer);
|
|
|
+ let toltime = this.recorder.duration; // 录音总时长
|
|
|
+ let fileSize = this.recorder.fileSize; // 录音总大小
|
|
|
+ // 录音结束,获取取录音数据
|
|
|
+ let wav = this.recorder.getWAVBlob(); // 获取 WAV 数据
|
|
|
+ // this.wavblob = wav;
|
|
|
+ this.microphoneStatus = false;
|
|
|
+ let reader = new window.FileReader();
|
|
|
+ reader.readAsDataURL(wav);
|
|
|
+ reader.onloadend = () => {
|
|
|
+ _this.recordList[_this.selectIndex].wavData = reader.result;
|
|
|
+ _this.recordList[_this.selectIndex].toltime = Math.floor(toltime);
|
|
|
+ _this.recordList[_this.selectIndex].fileSize = fileSize;
|
|
|
+
|
|
|
+ _this.wavblob = _this.recordList[_this.selectIndex].wavData;
|
|
|
+ _this.$emit("getWavblob", _this.wavblob);
|
|
|
+ _this.$emit(
|
|
|
+ "handleWav",
|
|
|
+ JSON.parse(JSON.stringify(_this.recordList)),
|
|
|
+ _this.tmIndex
|
|
|
+ );
|
|
|
+ if (this.recordList[this.selectIndex].selectData) {
|
|
|
+ this.$emit(
|
|
|
+ "getSelectData",
|
|
|
+ this.recordList[this.selectIndex].selectData
|
|
|
+ );
|
|
|
+ }
|
|
|
+ };
|
|
|
+ }
|
|
|
+ _this.$emit(
|
|
|
+ "getRerordStatus",
|
|
|
+ !_this.microphoneStatus && _this.recordList.length > 0
|
|
|
+ );
|
|
|
+ _this.$emit("getMicrophoneStatus", _this.microphoneStatus);
|
|
|
+ },
|
|
|
+ playmicrophone(totalTimes) {
|
|
|
+ if (this.hasMicro) {
|
|
|
+ this.isPlaying = true;
|
|
|
+ if (this.selectIndex || this.selectIndex == 0) {
|
|
|
+ let totalTime = totalTimes;
|
|
|
+ let _this = this;
|
|
|
+ if (!this.audio.paused) {
|
|
|
+ this.audio.pause();
|
|
|
+ clearInterval(_this.timer);
|
|
|
+ } else if (this.audio.paused && _this.oldIndex == _this.selectIndex) {
|
|
|
+ _this.audio.play();
|
|
|
+ if (_this.recordtime == 0) {
|
|
|
+ _this.recordtime = totalTimes;
|
|
|
+ _this.playtime = 0;
|
|
|
+ }
|
|
|
+ _this.timer = setInterval(() => {
|
|
|
+ if (_this.playtime < totalTime) {
|
|
|
+ _this.playtime++;
|
|
|
+ _this.recordtime = totalTime - _this.playtime;
|
|
|
+ } else {
|
|
|
+ clearInterval(_this.timer);
|
|
|
+ }
|
|
|
+ }, 1000);
|
|
|
+ } else {
|
|
|
+ _this.audio.pause();
|
|
|
+ _this.audio.load();
|
|
|
+ _this.audio.src = _this.wavblob;
|
|
|
+ _this.oldIndex = _this.selectIndex;
|
|
|
+ _this.audio.play();
|
|
|
+ _this.playtime = 0;
|
|
|
+ _this.recordtime = totalTime;
|
|
|
+ clearInterval(_this.timer);
|
|
|
+ _this.timer = setInterval(() => {
|
|
|
+ if (_this.playtime < totalTime) {
|
|
|
+ _this.playtime++;
|
|
|
+ _this.recordtime = totalTime - _this.playtime;
|
|
|
+ } else {
|
|
|
+ clearInterval(_this.timer);
|
|
|
+ }
|
|
|
+ }, 1000);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // 高亮初始值
|
|
|
+ handleActive() {
|
|
|
+ if (this.wavData) {
|
|
|
+ // this.hasMicro = "normal";
|
|
|
+ } else {
|
|
|
+ this.hasMicro = "";
|
|
|
+ }
|
|
|
+ },
|
|
|
+ changeStatus(status) {
|
|
|
+ this.hasMicro = status;
|
|
|
+ },
|
|
|
+ // 格式化录音时长
|
|
|
+ handleDateTime(time) {
|
|
|
+ if (parseInt(time / 60) < 10) {
|
|
|
+ time =
|
|
|
+ ("0" + parseInt(time / 60)).substring(
|
|
|
+ ("0" + parseInt(time / 60)).length - 2
|
|
|
+ ) +
|
|
|
+ ":" +
|
|
|
+ ("0" + (time % 60)).substring(("0" + (time % 60)).length - 2);
|
|
|
+ } else {
|
|
|
+ time =
|
|
|
+ parseInt(time / 60) +
|
|
|
+ ":" +
|
|
|
+ ("0" + (time % 60)).substring(("0" + (time % 60)).length - 2);
|
|
|
+ }
|
|
|
+ return time;
|
|
|
+ },
|
|
|
+ handleChangeRecord(index) {
|
|
|
+ this.recordtime = this.recordList[index].toltime;
|
|
|
+ this.wavblob = this.recordList[index].wavData;
|
|
|
+ this.hasMicro = "normal";
|
|
|
+ clearInterval(this.timer);
|
|
|
+ this.audio.pause();
|
|
|
+ this.oldIndex = null;
|
|
|
+ this.$emit("getWavblob", this.wavblob);
|
|
|
+ if (this.recordList[index].selectData) {
|
|
|
+ this.$emit("getSelectData", this.recordList[index].selectData);
|
|
|
+ }
|
|
|
+ this.$emit("sentPause", false);
|
|
|
+ },
|
|
|
+ handleDelete() {
|
|
|
+ if (this.hasMicro) {
|
|
|
+ if (this.selectIndex || this.selectIndex == 0) {
|
|
|
+ this.recordList.splice(this.selectIndex, 1);
|
|
|
+ this.$emit(
|
|
|
+ "handleWav",
|
|
|
+ JSON.parse(JSON.stringify(this.recordList)),
|
|
|
+ this.tmIndex
|
|
|
+ );
|
|
|
+ this.selectIndex =
|
|
|
+ this.recordList.length > 0 ? this.recordList.length - 1 : null;
|
|
|
+ this.hasMicro = this.recordList.length > 0 ? "normal" : "";
|
|
|
+ this.recordtime =
|
|
|
+ this.recordList.length > 0
|
|
|
+ ? this.recordList[this.selectIndex].toltime
|
|
|
+ : 0;
|
|
|
+ this.audio.pause();
|
|
|
+ this.audio = new window.Audio();
|
|
|
+ this.wavblob =
|
|
|
+ this.recordList.length > 0
|
|
|
+ ? this.recordList[this.selectIndex].wavData
|
|
|
+ : null;
|
|
|
+ this.oldIndex = null;
|
|
|
+ this.isPlaying = false;
|
|
|
+ clearInterval(this.timer);
|
|
|
+ }
|
|
|
+ this.$emit("getRerordStatus", this.recordList.length > 0);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ }, // 如果页面有keep-alive缓存功能,这个函数会触发
|
|
|
+};
|
|
|
+</script>
|
|
|
+<style lang="scss" scoped>
|
|
|
+//@import url(); 引入公共css类
|
|
|
+.NNPE-Book-record {
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: center;
|
|
|
+ // max-width: 292px;
|
|
|
+ height: 32px;
|
|
|
+ // border: 1px solid rgba(0, 0, 0, 0.1);
|
|
|
+ // border-radius: 8px;
|
|
|
+ .playBack {
|
|
|
+ width: 24px;
|
|
|
+ height: 24px;
|
|
|
+ margin-left: 8px;
|
|
|
+ // background: url("../../../assets/icon/play-24-normal-Black.png") center
|
|
|
+ // no-repeat;
|
|
|
+ // background-size: 100%;
|
|
|
+ cursor: pointer;
|
|
|
+ // &.normal {
|
|
|
+ // background: url("../../../assets/icon/play-24-normal-Black.png") center
|
|
|
+ // no-repeat;
|
|
|
+ // background-size: 100%;
|
|
|
+ // }
|
|
|
+ // &.active {
|
|
|
+ // background: url("../../../assets/icon/pause-24-normal-Black.png") center
|
|
|
+ // no-repeat;
|
|
|
+ // background-size: 100%;
|
|
|
+ // }
|
|
|
+ }
|
|
|
+ .playBack-white {
|
|
|
+ width: 24px;
|
|
|
+ height: 24px;
|
|
|
+ margin-left: 8px;
|
|
|
+ // background: url("../../../assets/icon/play-24-normal-white.png") center
|
|
|
+ // no-repeat;
|
|
|
+ // background-size: 100%;
|
|
|
+ cursor: pointer;
|
|
|
+ // &.normal {
|
|
|
+ // background: url("../../../assets/icon/play-24-normal-white.png") center
|
|
|
+ // no-repeat;
|
|
|
+ // background-size: 100%;
|
|
|
+ // }
|
|
|
+ // &.active {
|
|
|
+ // background: url("../../../assets/icon/pause-24-normal-white.png") center
|
|
|
+ // no-repeat;
|
|
|
+ // background-size: 100%;
|
|
|
+ // }
|
|
|
+ // &.disabled {
|
|
|
+ // background: url("../../../assets/icon/play-16-disable-Black.png") center
|
|
|
+ // no-repeat;
|
|
|
+ // background-size: 100%;
|
|
|
+ // }
|
|
|
+ .record-time {
|
|
|
+ color: #fff;
|
|
|
+ &.disabled {
|
|
|
+ color: rgba(0, 0, 0, 0.3);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .line {
|
|
|
+ width: 1px;
|
|
|
+ height: 16px;
|
|
|
+ background: rgba(0, 0, 0, 0.85);
|
|
|
+ opacity: 0.2;
|
|
|
+ margin-left: 8px;
|
|
|
+ }
|
|
|
+ .record-main {
|
|
|
+ width: 162px;
|
|
|
+ height: 136px;
|
|
|
+ box-sizing: border-box;
|
|
|
+ padding: 40px 8px 40px 40px;
|
|
|
+ &.active {
|
|
|
+ // background: url("../../../assets/NPC/vocie-gif.png") no-repeat left top;
|
|
|
+ background-size: 100% 100%;
|
|
|
+ }
|
|
|
+ &-inner {
|
|
|
+ padding: 16px 16px;
|
|
|
+ box-sizing: border-box;
|
|
|
+ width: fit-content;
|
|
|
+ height: 56px;
|
|
|
+ border-radius: 40px;
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: center;
|
|
|
+ box-sizing: border-box;
|
|
|
+ background: #de4444;
|
|
|
+ border: 1px solid rgba(0, 0, 0, 0.1);
|
|
|
+ cursor: pointer;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .record {
|
|
|
+ width: 24px;
|
|
|
+ height: 24px;
|
|
|
+ // background: url("../../../assets/NPC/voice-24.png") center no-repeat;
|
|
|
+ background-size: 100%;
|
|
|
+ }
|
|
|
+ .record-time {
|
|
|
+ color: #fff;
|
|
|
+ font-size: 16px;
|
|
|
+ line-height: 150%;
|
|
|
+ margin-left: 8px;
|
|
|
+ font-family: "robot";
|
|
|
+ font-weight: bold;
|
|
|
+ font-size: 16px;
|
|
|
+ line-height: 24px;
|
|
|
+ margin-left: 16px;
|
|
|
+ &.disabled {
|
|
|
+ color: rgba(0, 0, 0, 0.3);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .hasRecord {
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: center;
|
|
|
+ height: 56px;
|
|
|
+ padding: 16px 0 16px 16px;
|
|
|
+ border-radius: 40px 0 0 40px;
|
|
|
+ border: 1px solid rgba(0, 0, 0, 0.1);
|
|
|
+ border-right: 0px solid rgba(0, 0, 0, 0.1);
|
|
|
+ box-sizing: border-box;
|
|
|
+
|
|
|
+ .record-time {
|
|
|
+ width: 46px;
|
|
|
+ color: #000000;
|
|
|
+ text-align: center;
|
|
|
+ &.disabled {
|
|
|
+ color: rgba(0, 0, 0, 0.3);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .el-select {
|
|
|
+ margin-left: 16px;
|
|
|
+ }
|
|
|
+ .record-delete {
|
|
|
+ width: 24px;
|
|
|
+ height: 24px;
|
|
|
+ margin-left: 16px;
|
|
|
+ // background: url("../../../assets/NPC/del-24.png") center no-repeat;
|
|
|
+ background-size: 100%;
|
|
|
+ display: block;
|
|
|
+ &-disabled {
|
|
|
+ // background: url("../../../assets/NPC/del-24.png") center no-repeat;
|
|
|
+ background-size: 100%;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ &-white {
|
|
|
+ background: rgba(255, 255, 255, 0.1);
|
|
|
+ border: 1px solid rgba(0, 0, 0, 0.1);
|
|
|
+ border-right: 0;
|
|
|
+ .record-time {
|
|
|
+ color: #fff;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ &-answer {
|
|
|
+ padding: 16px;
|
|
|
+ border-radius: 40px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|
|
|
+<style lang="scss">
|
|
|
+.NNPE-Book-record {
|
|
|
+ .hasRecord {
|
|
|
+ .el-select {
|
|
|
+ flex: 1;
|
|
|
+ height: 24px;
|
|
|
+ &.proSelect {
|
|
|
+ width: 78px;
|
|
|
+ }
|
|
|
+ .el-input__inner {
|
|
|
+ width: 115px;
|
|
|
+ box-sizing: border-box;
|
|
|
+ height: 24px;
|
|
|
+ line-height: 22px;
|
|
|
+ border: 0px solid rgba(0, 0, 0, 0.1);
|
|
|
+ box-sizing: border-box;
|
|
|
+ border-radius: 4px;
|
|
|
+ padding: 0px 11px;
|
|
|
+ font-size: 14px;
|
|
|
+ color: rgba(0, 0, 0, 0.85);
|
|
|
+ }
|
|
|
+ .el-input {
|
|
|
+ .el-select__caret {
|
|
|
+ color: #000;
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: center;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .el-input__icon {
|
|
|
+ width: 16px;
|
|
|
+ }
|
|
|
+ .el-input__suffix {
|
|
|
+ right: 3px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ &-white {
|
|
|
+ .el-select {
|
|
|
+ .el-input__inner {
|
|
|
+ background: rgba(255, 255, 255, 0.1);
|
|
|
+ border-radius: 4px;
|
|
|
+ color: #fff;
|
|
|
+ }
|
|
|
+ .el-input {
|
|
|
+ .el-select__caret {
|
|
|
+ color: #000;
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: center;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .el-input__icon {
|
|
|
+ width: 16px;
|
|
|
+ }
|
|
|
+ .el-input__suffix {
|
|
|
+ right: 3px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|