Voice.vue 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. <template>
  2. <button class="cu-btn voice-btn" v-if="!disabled" ref="v-btn"
  3. :class="voice.recording ? 'active' : ''"
  4. @touchstart.stop.prevent="startVoice" @touchmove.stop.prevent="moveVoice" @touchend.stop="endVoice"
  5. @touchcancel.stop="cancelVoice">
  6. {{ voice.voiceTitle }}
  7. </button>
  8. <button class="cu-btn" v-if="disabled" ref="v-btn"
  9. :style="{ background: voice.recording ? '#c7c6c6' : '#FFFFFF',flex:'1' }">
  10. {{ times }}秒后可录音
  11. </button>
  12. </template>
  13. <script setup lang="ts">
  14. import {onMounted, reactive, ref} from "vue";
  15. import MessageType from "@/utils/MessageType";
  16. import upload from '@/api/UploadApi';
  17. import MessageUtils from "@/utils/MessageUtils";
  18. const disabled = ref(false);
  19. const times = ref(5);
  20. const interval = ref(-1);
  21. const emit = defineEmits(["uploadCallback"]);
  22. const voice = reactive({
  23. voiceTitle: '按住 说话',
  24. Recorder: uni.getRecorderManager(),
  25. recording: false,
  26. //标识是否正在录音
  27. isStopVoice: false,
  28. //加锁 防止点击过快引起的当录音正在准备(还没有开始录音)的时候,却调用了stop方法但并不能阻止录音的问题
  29. voiceInterval: -1,
  30. voiceTime: 0, //总共录音时长
  31. canSend: true, //是否可以发送
  32. PointY: 0, //坐标位置
  33. showFunBtn: false, //是否展示功能型按钮
  34. AudioExam: null //正在播放音频的实例
  35. })
  36. onMounted(() => {
  37. //录音开始事件
  38. voice.Recorder.onStart(() => {
  39. beginVoice();
  40. });
  41. //录音结束事件
  42. voice.Recorder.onStop(res => {
  43. clearInterval(voice.voiceInterval);
  44. if (voice.voiceTime >= 1) {
  45. handleRecorder(res);
  46. } else {
  47. voice.recording = false;
  48. MessageUtils.message('录音时间太短')
  49. }
  50. });
  51. })
  52. //准备开始录音
  53. const startVoice = (e: TouchEvent) => {
  54. voice.recording = true;
  55. voice.isStopVoice = false;
  56. voice.canSend = true;
  57. voice.PointY = e.touches[0].clientY;
  58. voice.Recorder.start({
  59. format: 'mp3'
  60. });
  61. }
  62. //录音已经开始
  63. const beginVoice = () => {
  64. uni.showLoading({
  65. title: '正在录音'
  66. })
  67. voice.voiceTitle = '松开 结束'
  68. voice.voiceInterval = setInterval(() => {
  69. voice.voiceTime++;
  70. }, 1000)
  71. }
  72. //move 正在录音中
  73. const moveVoice = (e: TouchEvent) => {
  74. const pointY = e.touches[0].clientY
  75. const slideY = voice.PointY - pointY;
  76. if (slideY > uni.upx2px(120)) {
  77. voice.canSend = false;
  78. } else if (slideY > uni.upx2px(60)) {
  79. voice.canSend = true;
  80. }
  81. }
  82. //结束录音
  83. const endVoice = () => {
  84. uni.hideLoading()
  85. voice.isStopVoice = true; //加锁 确保已经结束录音并不会录制
  86. setTimeout(function () {
  87. voice.Recorder.stop();
  88. }, 200)
  89. voice.voiceTitle = '按住 说话'
  90. disabled.value = true;
  91. setTimeout(function () {
  92. disabled.value = false;
  93. clearInterval(interval.value)
  94. times.value = 5;
  95. }, 5000)
  96. interval.value = setInterval(function () {
  97. times.value--;
  98. }, 1000)
  99. }
  100. //录音被打断
  101. const cancelVoice = () => {
  102. voice.voiceTime = 0;
  103. voice.voiceTitle = '按住 说话';
  104. voice.canSend = false;
  105. voice.Recorder.stop();
  106. }
  107. //处理录音文件
  108. const handleRecorder = ({tempFilePath}: any) => {
  109. voice.recording = false;
  110. uni.showLoading({
  111. title: '正在上传'
  112. })
  113. upload(tempFilePath)
  114. .then((res: any) => {
  115. const extend = {url: res.url, time: Math.ceil(voice.voiceTime)};
  116. voice.voiceTime = 0;
  117. emit('uploadCallback', extend, MessageType.voice)
  118. })
  119. .finally(() => {
  120. uni.hideLoading()
  121. })
  122. }
  123. </script>
  124. <style scoped>
  125. .voice-btn {
  126. flex: 1;
  127. display: flex;
  128. align-items: center;
  129. justify-content: center;
  130. background-color: #fff;
  131. padding: 20upx;
  132. box-shadow: 0.0625rem 0.0625rem 0.1875rem rgba(0, 0, 0, .2);
  133. position: relative;
  134. margin: 0 .1875rem !important;
  135. height: 100%;
  136. }
  137. .voice-btn.active {
  138. background-color: #57d757;
  139. color: #ffffff;
  140. }
  141. </style>