AudioField.vue 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. <!--
  2. 作者:GuoZhiBo
  3. 时间:2020-05-19
  4. 描述:录音和播放语音插件
  5. -->
  6. <template>
  7. <div>
  8. <div v-if="isReadonly == 'false'" style="margin-bottom: 10px">
  9. <button
  10. type="button"
  11. class="btn btn-info"
  12. @mousedown.prevent="mouseStart"
  13. >
  14. <span class="glyphicon glyphicon-volume-up" aria-hidden="true" />
  15. {{ tiptext }}
  16. </button>
  17. </div>
  18. <template v-if="srcArray.length > 0">
  19. <div
  20. v-for="(item, index) in srcArray"
  21. :key="index"
  22. style="margin-bottom: 10px; display: inline-flex; align-items: center"
  23. >
  24. <AuthAudio :audio-src="getUrl(item)" :token="token" />
  25. <button
  26. v-if="isReadonly == 'false'"
  27. type="button"
  28. class="btn btn-link"
  29. style="flex: 1"
  30. @click="deleteAudioSrc(index)"
  31. >
  32. <span class="glyphicon glyphicon-remove" aria-hidden="true" /> 删除
  33. </button>
  34. <div class="clearfix" />
  35. </div>
  36. </template>
  37. </div>
  38. </template>
  39. <script type="text/javascript">
  40. var Common = require('../common/Common.js');
  41. import recording from './recorder.js';
  42. var AuthAudio = require('./AuthAudio.vue').default;
  43. export default {
  44. name: 'MRecorder',
  45. components: {
  46. AuthAudio,
  47. },
  48. // props: ['className', 'srcArray', 'token', 'isReadonly'],
  49. props: {
  50. className: {
  51. type: String,
  52. default: null,
  53. },
  54. srcArray: {
  55. type: Array,
  56. default() {
  57. return [];
  58. },
  59. },
  60. token: {
  61. type: String,
  62. default: null,
  63. },
  64. isReadonly: {
  65. type: Boolean,
  66. },
  67. },
  68. data() {
  69. return {
  70. src: null,
  71. mcounter: 0, // 累积时间
  72. recording: false, // 标记是否在录音
  73. intervaltimerid: '', // 间隔时间定时器编号
  74. tiptext: '按住说话', // 提示文字
  75. mediaRecorder: null, // 录音笔
  76. mediaConstraints: {
  77. audio: true,
  78. },
  79. isTouchEnd: false, //是否移动结束事件
  80. num: 60, // 按住说话时间
  81. recorder: null,
  82. interval: '',
  83. audioFileList: [], // 上传语音列表
  84. startTime: '', // 语音开始时间
  85. endTime: '', // 语音结束
  86. flag: true,
  87. };
  88. },
  89. unmounted: function () {
  90. if (this.recorder) {
  91. this.recorder.stop();
  92. this.clearTimer();
  93. }
  94. },
  95. methods: {
  96. getUrl: function (item) {
  97. var _self = this;
  98. var url =
  99. Common.getApiURL('file/audioDownload') +
  100. '?className=' +
  101. _self.className +
  102. '&fileName=' +
  103. item;
  104. return url;
  105. },
  106. /**
  107. * 清除定时器
  108. */
  109. clearTimer: function () {
  110. if (this.interval) {
  111. this.num = 60;
  112. clearInterval(this.interval);
  113. }
  114. },
  115. /**
  116. * 长按说话
  117. */
  118. mouseStart: function () {
  119. var _self = this;
  120. this.clearTimer();
  121. this.startTime = new Date().getTime();
  122. recording.get(rec => {
  123. // 当首次按下时,要获取浏览器的麦克风权限,所以这时要做一个判断处理
  124. if (rec) {
  125. if (this.flag) {
  126. this.recorder = rec;
  127. this.interval = setInterval(() => {
  128. this.mcounter = this.mcounter + 1;
  129. this.tiptext = '录音中 ' + this.mcounter + 's';
  130. if (this.num <= 0) {
  131. rec.stop();
  132. this.num = 60;
  133. this.clearTimer();
  134. } else {
  135. this.num--;
  136. this.time = '松开结束(' + this.num + '秒)';
  137. rec.start();
  138. }
  139. }, 1000);
  140. this.flag = false;
  141. } else {
  142. this.flag = true;
  143. this.clearTimer();
  144. this.endTime = new Date().getTime();
  145. this.recorder.stop();
  146. rec.stop();
  147. // 重置说话时间
  148. this.tiptext = '按住说话';
  149. if (this.mcounter > 0) {
  150. this.num = 60;
  151. // 获取语音二进制文件
  152. let blob = this.recorder.getBlob();
  153. this.audioUpload(blob);
  154. this.mcounter = 0;
  155. }
  156. }
  157. }
  158. });
  159. },
  160. /**
  161. * 松开时上传语音
  162. */
  163. mouseEnd: function () {
  164. var _self = this;
  165. this.clearTimer();
  166. this.endTime = new Date().getTime();
  167. if (this.recorder) {
  168. this.recorder.stop();
  169. this.num = 60;
  170. // 重置说话时间
  171. this.tiptext = '按住说话';
  172. // 获取语音二进制文件
  173. let blob = this.recorder.getBlob();
  174. this.audioUpload(blob);
  175. }
  176. },
  177. /**
  178. * 删除语音
  179. * @param {Object} index
  180. */
  181. deleteAudioSrc: function (index) {
  182. var _self = this;
  183. _self.$emit('deleteAudioSrc', index);
  184. },
  185. /**
  186. * 长按
  187. */
  188. longTap: function () {
  189. var _self = this;
  190. _self.mouseStart();
  191. console.log('开始录音');
  192. },
  193. /**
  194. * 移动
  195. */
  196. touchMove: function () {
  197. var _self = this;
  198. console.log('取消录音');
  199. _self.isTouchEnd = true;
  200. _self.mouseEnd();
  201. },
  202. /**
  203. * 结束
  204. */
  205. touchEnd: function () {
  206. var _self = this;
  207. console.log('结束录音');
  208. _self.mouseEnd();
  209. },
  210. /**
  211. * 上传语音
  212. * @param {Object} blob
  213. */
  214. audioUpload: function (blob) {
  215. var fileName;
  216. var _self = this;
  217. if (_self.isTouchEnd == false) {
  218. // 将获取的二进制对象转为二进制文件流
  219. let file = new File([blob], 'test.mp3', {
  220. type: 'audio/mp3',
  221. lastModified: Date.now(),
  222. });
  223. // create FormData
  224. var formData = new FormData();
  225. formData.append('fileName', file.name);
  226. formData.append('files', file);
  227. formData.append('className', _self.className);
  228. $.ajax({
  229. url: Common.getApiURL('file/audioUpload'),
  230. type: 'post',
  231. beforeSend: function (request) {
  232. Common.addTokenToRequest(request);
  233. },
  234. data: formData,
  235. contentType: false,
  236. processData: false,
  237. success: function (data) {
  238. if (data != 'error') {
  239. fileName = data.substring(data.indexOf(':') + 1);
  240. const url = URL.createObjectURL(blob);
  241. _self.$emit('handleStop', {
  242. url: url,
  243. mblob: blob,
  244. fileName: fileName,
  245. });
  246. }
  247. },
  248. error: function (XMLHttpRequest, textStatus, errorThrown) {
  249. Common.processException(XMLHttpRequest, textStatus, errorThrown);
  250. },
  251. });
  252. console.log('录音成功:' + fileName);
  253. } else {
  254. console.log('取消录音');
  255. }
  256. _self.isTouchEnd = false;
  257. },
  258. },
  259. };
  260. </script>
  261. <style scoped>
  262. .wrapper {
  263. text-align: center;
  264. }
  265. .mrecorder {
  266. width: 40px;
  267. height: 40px;
  268. font-size: 40px;
  269. }
  270. .anirecorder {
  271. position: relative;
  272. animation: mymove 5s infinite;
  273. -webkit-animation: mymove 5s infinite;
  274. animation-direction: alternate;
  275. animation-timing-function: ease-in-out;
  276. /*safari & chrome*/
  277. -webkit-animation-direction: alternate;
  278. -webkit-transition-timing-function: ease-in-out;
  279. }
  280. @keyframes mymove {
  281. 0% {
  282. transform: scale(1);
  283. /*开始为原始大小*/
  284. }
  285. 25% {
  286. transform: scale(1.1);
  287. /*放大1.1倍*/
  288. }
  289. 50% {
  290. transform: scale(0.9);
  291. }
  292. 75% {
  293. transform: scale(1.1);
  294. }
  295. }
  296. @-webkit-keyframes mymove
  297. /*Safari and Chrome*/ {
  298. 0% {
  299. transform: scale(1);
  300. /*开始为原始大小*/
  301. }
  302. 25% {
  303. transform: scale(1.1);
  304. /*放大1.1倍*/
  305. }
  306. 50% {
  307. transform: scale(0.9);
  308. }
  309. 75% {
  310. transform: scale(1.1);
  311. }
  312. }
  313. </style>