| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326 |
- <template>
- <view class="ss-recording-wrapper">
- <view class="ss-recording" @tap="clickMicBtn">
- <uni-icons :type="micIcon" size="24" color="#101333"></uni-icons>
- </view>
- <uni-popup ref="recordPopup" background-color="#fff" :is-mask-click="false">
- <view class="record-dialog">
- <view class="record-title">录音将在{{ countDown }}秒后自动结束</view>
- <view class="record-animation" :class="'show-bg-' + decibel">
- <view class="mic p-center">
- <uni-icons type="mic" :size="24" color="#2943d6" />
- </view>
- <template v-for="i in 10">
- <view class="mic-bg p-center" :class="'bg-' + i"></view>
- </template>
- </view>
- <view class="record-buttons">
- <button class="record-cancel" @tap="cancelRecord">取消</button>
- <button class="record-start" @tap="recorded">
- <view v-if="recordStatus == 3" class="record-loading">
- <uni-icons type="spinner-cycle" size="32rpx" color="#ffffff" />
- </view>
- <view v-if="recordStatus == 2" class="record-stop"></view>
- {{ recordStatus == 2 ? '我说完了' : recordStatus == 3 ? '音频处理中' : '开始录音' }}
- </button>
- </view>
- </view>
- </uni-popup>
- </view>
- </template>
- <script>
- import { RequestApi } from '@/api/requestApi';
- export default {
- data() {
- return {
- recordFlag: true,
- recordStatus: 1, // 1、待开始录音;2、录音中;3、处理中;
- recorderManager: null,
- isRecording: false,
- recordOption: {
- sampleRate: 8000, // 采样率
- numberOfChannels: 1, // 单声道
- format: 'mp3', // 格式为mp3获取原始音频数据
- frameSize: 1, // 帧大小
- frameBufferSize: 10 // 每次回调积累的帧数
- },
- recordDuration: 60,
- useTime: 0,
- countDownTimeout: null
- };
- },
- computed: {
- micIcon() {
- return this.recordFlag ? 'mic' : 'micoff';
- },
- maxDuration() {
- return this.recordDuration;
- },
- countDown() {
- let residue = this.maxDuration - parseInt(this.useTime / 10);
- return residue > 0 ? residue : 0;
- },
- decibel() {
- return this.useTime % 10;
- }
- },
- mounted() {
- this.canvasContext = uni.createCanvasContext('waveCanvas', this);
- this.initRecorder();
- },
- methods: {
- initRecorder() {
- this.recorderManager = uni.getRecorderManager();
- // 录音完成,上传解析
- this.recorderManager.onStop((res) => {
- this.stopCountDown();
- if (this.recordStatus == 2) {
- this.recordStatus = 3;
- console.log('临时文件路径:', res.tempFilePath);
- this.sendVideoFile(res.tempFilePath);
- }
- });
- },
- sendVideoFile(filePath) {
- RequestApi.transcribe(filePath)
- .then((res) => {
- this.recordStatus = 1;
- if (res.text == '(空内容)') {
- uni.showToast({
- title: '未检测到你说了什么',
- icon: 'none'
- });
- } else {
- this.$emit('setMessage', res.text);
- }
- })
- .catch((error) => {
- uni.showToast({
- title: '语音识别错误',
- icon: 'none'
- });
- })
- .finally(() => {
- if (this.$refs.recordPopup) {
- this.$refs.recordPopup.close();
- }
- });
- },
- clickMicBtn() {
- if (this.recordStatus == 1) {
- this.startRecord();
- }
- },
- startCountDown() {
- if (this.countDownTimeout) {
- clearTimeout(this.countDownTimeout);
- }
- this.countDownTimeout = setTimeout(() => {
- this.useTime++;
- this.countDownTimeout = null;
- if (this.useTime >= this.maxDuration * 10) {
- this.recorderManager.stop();
- } else {
- this.startCountDown();
- }
- }, 99);
- },
- stopCountDown() {
- if (this.countDownTimeout) {
- clearTimeout(this.countDownTimeout);
- }
- },
- startRecord() {
- uni.authorize({
- scope: 'scope.record',
- success: () => {
- this.recordFlag = true;
- this.isRecording = true;
- this.recordStatus = 2;
- this.recorderManager.start({
- ...this.recordOption,
- duration: this.recordDuration * 1000,
- success: () => {
- this.useTime = 0;
- this.startCountDown();
- if (this.$refs.recordPopup) {
- this.$refs.recordPopup.open('bottom');
- }
- },
- fail: () => {
- uni.showToast({
- title: '录音启动失败',
- icon: 'none'
- });
- }
- });
- },
- fail: () => {
- this.recordStatus = 1;
- this.recordFlag = false;
- uni.showToast({
- title: '请授权麦克风权限',
- icon: 'none'
- });
- }
- });
- },
- recorded() {
- if (this.recordStatus == 1) {
- this.startRecord();
- }
- if (this.recordStatus == 2) {
- this.recorderManager.stop();
- }
- },
- cancelRecord() {
- this.recordStatus = 1;
- this.recorderManager.stop();
- if (this.animationId) {
- cancelAnimationFrame(this.animationId);
- }
- if (this.$refs.recordPopup) {
- this.$refs.recordPopup.close();
- }
- }
- }
- };
- </script>
- <style scoped lang="scss">
- @keyframes spin {
- 0% {
- transform: rotate(0deg);
- }
- 100% {
- transform: rotate(360deg);
- }
- }
- .ss-recording-wrapper {
- display: inline-flex;
- align-items: center;
- }
- .ss-recording {
- display: inline-flex;
- align-items: center;
- }
- .record-dialog {
- background-color: #ffffff;
- overflow: hidden;
- padding: 32rpx;
- .record-title {
- font-size: 32rpx;
- color: #101333;
- margin-bottom: 32rpx;
- }
- .record-animation {
- height: 280rpx;
- margin-bottom: 32rpx;
- position: relative;
- .p-center {
- position: absolute;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- }
- .mic {
- width: 64rpx;
- height: 64rpx;
- border-radius: 64rpx;
- border: 4rpx solid #2943d6;
- line-height: 64rpx;
- text-align: center;
- z-index: 100;
- }
- @for $i from 1 through 10 {
- &.show-bg-#{$i} {
- @for $j from 1 through 10 {
- .mic-bg {
- &.bg-#{$j} {
- opacity: if($j < $i, 1, 0);
- }
- }
- }
- }
- }
- .mic-bg {
- border: 2rpx solid #2943d6;
- transition: opacity 100ms linear;
- @for $i from 1 through 10 {
- &.bg-#{$i} {
- z-index: 100 - $i;
- width: 80rpx + $i * 20;
- height: 80rpx + $i * 20;
- border-radius: 40rpx + $i * 10;
- border-color: rgba(41, 67, 214, 1 - ($i * 0.1));
- opacity: 0;
- }
- }
- }
- }
- .record-canvas {
- width: 100%;
- height: 240rpx;
- margin-bottom: 60rpx;
- }
- .record-buttons {
- display: flex;
- justify-content: center;
- .record-cancel {
- border-radius: 50rpx;
- border: 1px solid #2943d6;
- color: #2943d6;
- margin-right: 16rpx;
- padding: 0 56rpx;
- background-color: #ffffff;
- &::after {
- background-color: transparent;
- }
- }
- .record-start {
- display: flex;
- align-items: center;
- justify-content: center;
- border-radius: 50rpx;
- border: 0;
- color: #ffffff;
- background-color: #2943d6;
- padding: 0 32rpx;
- width: 340rpx;
- .record-stop {
- width: 32rpx;
- height: 32rpx;
- background-color: #ffffff;
- margin-right: 16rpx;
- border-radius: 6rpx;
- }
- .record-loading {
- margin-right: 8rpx;
- animation: spin 3s linear infinite;
- }
- &::after {
- background-color: transparent;
- }
- }
- }
- }
- </style>
|