addVoice.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458
  1. <template>
  2. <view class="add-voice-page">
  3. <van-field
  4. :value="form.title"
  5. placeholder="请输入语音标题"
  6. :border="false"
  7. clearable
  8. label="语音标题"
  9. />
  10. <van-cell
  11. title="品种"
  12. value="原油"
  13. :border="false"
  14. />
  15. <van-cell
  16. title="板块名称"
  17. value="每日原油播报"
  18. :border="false"
  19. />
  20. <view class="flex audio-box" v-if="recorderStatus==='stop'">
  21. <image
  22. :src="temAudio.paused?'../../../static/voice/pause.png':'../../../static/voice/playing.png'"
  23. mode="aspectFill"
  24. @click="handlePlayAudio"
  25. />
  26. <slider
  27. activeColor="#E6B77D"
  28. :max="temAudio.duration"
  29. :value="temAudio.curTime"
  30. @change="handleAudioSliderChange($event)"
  31. block-size="12"
  32. class="slider"
  33. />
  34. <text class="left-time">{{temAudio.curTime|formatTime}}</text>
  35. <text class="right-time">{{temAudio.duration|formatTime}}</text>
  36. <view class="del-btn" @click="handleDelRecord">
  37. <image src="./static/del.png" mode="aspectFill" />
  38. <text>删除</text>
  39. </view>
  40. </view>
  41. <view class="empty-voice-box" v-if="recorderStatus==='start'">
  42. <image src="./static/record.png" mode="aspectFill" />
  43. <view>无录音(录音时长超过十分钟自动结束)</view>
  44. </view>
  45. <view class="animat-box" v-if="recorderStatus==='doing'||recorderStatus==='pause'">
  46. <view class="con-box">
  47. <image :class="['img move1',recorderStatus==='doing'?'animat-run':'animat-pause']" src="./static/record-img.png" mode="widthFix" />
  48. <image :class="['img move2',recorderStatus==='doing'?'animat-run':'animat-pause']" src="./static/record-img.png" mode="widthFix" />
  49. <image :class="['img move3',recorderStatus==='doing'?'animat-run':'animat-pause']" src="./static/record-img.png" mode="widthFix" />
  50. </view>
  51. <view class="bot-text">{{time|formatTime}}</view>
  52. </view>
  53. <view class="sound-record-wrap" v-if="recorderStatus!=='stop'">
  54. <view class="top-text">点击开始录音</view>
  55. <image class="btn" :src="btnImg" mode="aspectFill" @click="handleClickBtn" />
  56. <view
  57. class="del-btn"
  58. :style="{color:recorderStatus==='doing'&&'#999'}"
  59. v-if="recorderStatus!=='start'"
  60. @click="handleResetRecorder"
  61. >删除</view>
  62. <view
  63. class="done-btn"
  64. v-if="recorderStatus!=='start'"
  65. @click="handleEndRecorder"
  66. >完成</view>
  67. </view>
  68. <view class="publish-btn" v-if="recorderStatus==='stop'">发布</view>
  69. </view>
  70. </template>
  71. <script>
  72. const recorderManager = wx.getRecorderManager();//录音实例
  73. const innerAudioContext = uni.createInnerAudioContext();//播放音频实例
  74. let TIMER=null//计时器
  75. export default {
  76. filters:{
  77. formatTime(e){
  78. let m=parseInt(e/60)
  79. let s=parseInt(e%60)
  80. return `${m>9?m:'0'+m}:${s>9?s:'0'+s}`
  81. }
  82. },
  83. computed:{
  84. btnImg(){
  85. if(this.recorderStatus==='start'){
  86. return './static/voice-start.png'
  87. }else if(this.recorderStatus==='doing'){
  88. return './static/voice-doing.png'
  89. }else if(this.recorderStatus==='stop'){
  90. return './static/voice-pause.png'
  91. }else if(this.recorderStatus==='pause'){
  92. return './static/voice-pause.png'
  93. }
  94. }
  95. },
  96. data() {
  97. return {
  98. form:{
  99. title:'',//语音标题
  100. },
  101. recorderStatus:'start',//当前录音状态 start开始 doing正在录音 stop停止录音 pause录音暂停
  102. time:0,
  103. isReset:false,//是否点击了重置
  104. temAudio:{
  105. url:'',//临时音频地址
  106. duration:'',//时长
  107. size:'',//大小
  108. curTime:0,//播放时当前播放的时间
  109. paused:true,
  110. },//临时音频文件信息
  111. }
  112. },
  113. onLoad(){
  114. // 调取用户授权使用麦克风
  115. uni.authorize({
  116. scope: 'scope.record',
  117. success() {}
  118. })
  119. this.listenVoice()
  120. this.listenAudio()
  121. },
  122. methods: {
  123. //录音事件
  124. listenVoice(){
  125. recorderManager.onStart(()=>{
  126. //录音开始监听事件
  127. console.log('开始录音');
  128. this.recorderStatus='doing'
  129. this.isReset=false
  130. if(!TIMER){
  131. TIMER=setInterval(() => {
  132. this.time++
  133. }, 1000);
  134. }
  135. })
  136. recorderManager.onPause(()=>{
  137. //录音暂停监听事件
  138. console.log('录音暂停');
  139. this.recorderStatus='pause'
  140. clearInterval(TIMER)
  141. TIMER=null
  142. })
  143. recorderManager.onResume(()=>{
  144. //录音继续监听事件
  145. console.log('录音继续');
  146. this.recorderStatus='doing'
  147. if(!TIMER){
  148. TIMER=setInterval(() => {
  149. this.time++
  150. }, 1000);
  151. }
  152. })
  153. recorderManager.onStop((e)=>{
  154. //录音结束监听事件
  155. console.log('录音结束',e);
  156. // 如果是点击重置(删除按钮)的 则不做结束处理
  157. if(!this.isReset){
  158. this.recorderStatus='stop'
  159. this.temAudio.url=e.tempFilePath
  160. this.temAudio.size=e.fileSize
  161. this.temAudio.duration=parseInt(e.duration/1000)
  162. }
  163. clearInterval(TIMER)
  164. TIMER=null
  165. })
  166. recorderManager.onError((e)=>{
  167. //录音事件错误监听
  168. console.log('录音错误哦',e);
  169. })
  170. },
  171. //点击录音操作按钮
  172. handleClickBtn(){
  173. if(this.recorderStatus==='start'){
  174. recorderManager.start({
  175. duration:600000
  176. })
  177. }
  178. if(this.recorderStatus==='doing'){
  179. recorderManager.pause()//暂停录音
  180. }
  181. if(this.recorderStatus==='pause'){
  182. recorderManager.resume()//继续录音
  183. }
  184. },
  185. //点击重置录音状态
  186. handleResetRecorder(){
  187. if(this.recorderStatus==='doing') return
  188. this.isReset=true
  189. recorderManager.stop()
  190. this.recorderStatus='start'
  191. },
  192. //点击完成录音
  193. handleEndRecorder(){
  194. //点击完成时还不是已结束录音状态
  195. this.isReset=false
  196. recorderManager.stop()
  197. },
  198. //拖动音频播放进度条
  199. handleAudioSliderChange(e){
  200. const value=e.detail.value
  201. innerAudioContext.seek(value)
  202. },
  203. //点击播放\暂停音频
  204. handlePlayAudio(){
  205. //没有初始化时
  206. if(!innerAudioContext.src){
  207. innerAudioContext.src=this.temAudio.url
  208. innerAudioContext.play()
  209. return
  210. }
  211. if(innerAudioContext.paused){
  212. innerAudioContext.play()
  213. }else{
  214. innerAudioContext.pause()
  215. }
  216. },
  217. //音频播放事件
  218. listenAudio(){
  219. innerAudioContext.onPlay(()=>{
  220. console.log('开始播放录音');
  221. this.temAudio.paused=false
  222. })
  223. innerAudioContext.onPause(()=>{
  224. console.log('录音播放暂停');
  225. this.temAudio.paused=true
  226. })
  227. innerAudioContext.onStop(()=>{
  228. console.log('录音播放停止');
  229. this.temAudio.paused=true
  230. innerAudioContext.src=''
  231. })
  232. innerAudioContext.onEnded(()=>{
  233. console.log('录音播放自然结束');
  234. this.temAudio.paused=true
  235. innerAudioContext.src=''
  236. })
  237. innerAudioContext.onTimeUpdate(()=>{
  238. this.temAudio.curTime=parseInt(innerAudioContext.currentTime)
  239. })
  240. },
  241. //删除录音记录
  242. handleDelRecord(){
  243. this.recorderStatus='start'
  244. this.isReset=true
  245. this.temAudio.url=''
  246. this.time=0
  247. TIMER=null
  248. }
  249. },
  250. }
  251. </script>
  252. <style>
  253. .add-voice-page .van-cell{
  254. border-bottom: 1px solid #e5e5e5;
  255. }
  256. .add-voice-page .van-field__label{
  257. font-size: 32rpx;
  258. color: #333;
  259. }
  260. .add-voice-page .van-cell__title{
  261. max-width: 6.2em;
  262. min-width: 6.2em;
  263. margin-right: 12px;
  264. font-size: 32rpx;
  265. color: #333;
  266. }
  267. .add-voice-page .van-cell__value{
  268. text-align: left;
  269. font-size: 32rpx;
  270. }
  271. page{
  272. padding-bottom:constant(safe-area-inset-bottom);
  273. padding-bottom:env(safe-area-inset-bottom);
  274. }
  275. </style>
  276. <style lang="scss" scoped>
  277. .add-voice-page{
  278. .empty-voice-box{
  279. height: 50vh;
  280. padding-top: 150rpx;
  281. image{
  282. width: 140rpx;
  283. height: 140rpx;
  284. margin-bottom: 20rpx;
  285. }
  286. text-align: center;
  287. color: #999999;
  288. }
  289. .sound-record-wrap{
  290. border-top: 1px solid #E6E6E6;
  291. padding-top: 126rpx;
  292. position: relative;
  293. .top-text{
  294. text-align: center;
  295. color: #EE3636;
  296. position: absolute;
  297. top: 50rpx;
  298. left: 50%;
  299. transform: translateX(-50%);
  300. }
  301. .btn{
  302. width: 118rpx;
  303. height: 118rpx;
  304. display: block;
  305. margin-left: auto;
  306. margin-right: auto;
  307. }
  308. .del-btn{
  309. position: absolute;
  310. left: 122rpx;
  311. bottom: 40rpx;
  312. font-size: 32rpx;
  313. color: #EE3636;
  314. }
  315. .done-btn{
  316. position: absolute;
  317. right: 122rpx;
  318. bottom: 40rpx;
  319. font-size: 32rpx;
  320. color: #EE3636;
  321. }
  322. }
  323. .audio-box{
  324. background-color: #FDF8F2;
  325. height: 123rpx;
  326. align-items: center;
  327. margin-left: 34rpx;
  328. margin-right: 34rpx;
  329. margin-top: 60rpx;
  330. margin-bottom: 180rpx;
  331. padding: 0 30rpx;
  332. position: relative;
  333. .left-time{
  334. position: absolute;
  335. bottom: 20rpx;
  336. left: 100rpx;
  337. color: #999999;
  338. font-size: 20rpx;
  339. }
  340. .right-time{
  341. position: absolute;
  342. bottom: 20rpx;
  343. right: 40rpx;
  344. color: #999999;
  345. font-size: 20rpx;
  346. }
  347. image{
  348. width: 40rpx;
  349. height: 48rpx;
  350. flex-shrink: 0;
  351. margin-right: 30rpx;
  352. }
  353. .slider{
  354. flex: 1;
  355. margin: 0 10rpx;
  356. }
  357. .del-btn{
  358. display: flex;
  359. align-items: center;
  360. color: #999999;
  361. font-size: 28rpx;
  362. position: absolute;
  363. bottom: -50rpx;
  364. right: 0;
  365. image{
  366. width: 32rpx;
  367. height: 35rpx;
  368. margin-right: 10rpx;
  369. }
  370. }
  371. }
  372. .publish-btn{
  373. width: 390rpx;
  374. height: 80rpx;
  375. text-align: center;
  376. line-height: 80rpx;
  377. color: #fff;
  378. background: #E6B77D;
  379. font-size: 32rpx;
  380. border-radius: 40px;
  381. margin-left: auto;
  382. margin-right: auto;
  383. }
  384. }
  385. .animat-box{
  386. height: 50vh;
  387. .con-box{
  388. height: 80%;
  389. background-color: #FAFAFA;
  390. position: relative;
  391. }
  392. .bot-text{
  393. font-size: 60rpx;
  394. padding-top: 36rpx;
  395. text-align: center;
  396. }
  397. .img{
  398. position: absolute;
  399. width: 100vw;
  400. top: 27%;
  401. transform: translateX(100vw);
  402. }
  403. .move1{
  404. animation: move 30s linear infinite;
  405. }
  406. .move2{
  407. animation: move 30s 10s linear infinite;
  408. }
  409. .move3{
  410. animation: move 30s 20s linear infinite;
  411. }
  412. .animat-pause{
  413. animation-play-state: paused;
  414. }
  415. .animat-run{
  416. animation-play-state: running;
  417. }
  418. @keyframes move {
  419. 0%{
  420. transform: translateX(100vw);
  421. }
  422. 100%{
  423. transform: translateX(-200vw);
  424. }
  425. }
  426. }
  427. </style>