Browse Source

mp:前端 message 增加 voice 语音的播放

YunaiV 2 years ago
parent
commit
94178998e5

+ 1 - 0
yudao-ui-admin/package.json

@@ -43,6 +43,7 @@
     "@babel/parser": "7.18.4",
     "@riophae/vue-treeselect": "0.4.0",
     "axios": "0.27.2",
+    "benz-amr-recorder": "^1.1.5",
     "bpmn-js-token-simulation": "0.10.0",
     "clipboard": "2.0.8",
     "core-js": "^3.26.0",

+ 1 - 1
yudao-ui-admin/src/views/mp/components/wx-video-play/main.vue

@@ -1,7 +1,7 @@
 <!--
   - Copyright (C) 2018-2019
   - All rights reserved, Designed By www.joolun.com
-  芋
+  芋道源码
   ① bug 修复:
     1)joolun 的做法:使用 mediaId 从微信公众号,下载对应的 mp4 素材,从而播放内容;
       存在的问题:mediaId 有效期是 3 天,超过时间后无法播放

+ 97 - 0
yudao-ui-admin/src/views/mp/components/wx-voice-play/main.vue

@@ -0,0 +1,97 @@
+<!--
+  - Copyright (C) 2018-2019
+  - All rights reserved, Designed By www.joolun.com
+   芋道源码:
+  ① bug 修复:
+    1)joolun 的做法:使用 mediaId 从微信公众号,下载对应的 mp4 素材,从而播放内容;
+      存在的问题:mediaId 有效期是 3 天,超过时间后无法播放
+    2)重构后的做法:后端接收到微信公众号的视频消息后,将视频消息的 media_id 的文件内容保存到文件服务器中,这样前端可以直接使用 URL 播放。
+  ② 代码优化:将 props 中的 objData 调成为 data 中对应的属性,并补充相关注释
+-->
+<template>
+  <div class="wx-voice-div" @click="playVoice">
+    <i :class="playing !== true ? 'el-icon-video-play': 'el-icon-video-pause'">
+      <span class="amr-duration" v-if="duration">{{ duration }}</span>
+    </i>
+    <div v-if="content">
+      <el-tag type="success" size="mini">语音识别</el-tag>
+      {{ content }}
+    </div>
+  </div>
+</template>
+
+<script>
+// 因为微信语音是 amr 格式,所以需要用到 amr 解码器:https://www.npmjs.com/package/benz-amr-recorder
+const BenzAMRRecorder = require('benz-amr-recorder')
+
+export default {
+  name: "wxVoicePlayer",
+  props: {
+    url: { // 语音地址,例如说:https://www.iocoder.cn/xxx.amr
+      type: String,
+      required: true
+    },
+    content: { // 语音文本
+      type: String,
+      required: false
+    }
+  },
+  data() {
+    return {
+      amr: undefined, // BenzAMRRecorder 对象
+      playing: false, // 是否在播放中
+      duration: undefined, // 播放时长
+    }
+  },
+  methods:{
+    playVoice() {
+      debugger
+      // 情况一:未初始化,则创建 BenzAMRRecorder
+      if (this.amr === undefined){
+        this.amrInit();
+        return;
+      }
+
+      if (this.amr.isPlaying()) {
+        this.amrStop();
+      } else {
+        this.amrPlay();
+      }
+    },
+    amrInit() {
+      const amr = new BenzAMRRecorder();
+      this.amr = amr;
+      // 设置播放
+      const that = this
+      amr.initWithUrl(this.url).then(function() {
+        that.amrPlay()
+        that.duration = amr.getDuration();
+      })
+      // 监听暂停
+      amr.onEnded(function() {
+        that.playing = false;
+      })
+    },
+    amrPlay() {
+      this.playing = true;
+      this.amr.play()
+    },
+    amrStop() {
+      this.playing = false;
+      this.amr.stop()
+    },
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+  .wx-voice-div {
+    padding: 5px;
+    background-color: #eaeaea;
+    border-radius: 10px;
+  }
+  .amr-duration {
+    font-size: 11px;
+    margin-left: 5px;
+  }
+</style>

+ 5 - 1
yudao-ui-admin/src/views/mp/message/index.vue

@@ -70,7 +70,9 @@
           </div>
           <!-- 【消息】区域 -->
           <div v-else-if="scope.row.type === 'text'">{{ scope.row.content }}</div>
-          <!-- TODO 语音 -->
+          <div v-else-if="scope.row.type === 'voice'">
+            <wx-voice-player :url="scope.row.mediaUrl" :content="scope.row.recognition" />
+          </div>
           <div v-else-if="scope.row.type === 'image'">
             <a target="_blank" :href="scope.row.mediaUrl">
               <img :src="scope.row.mediaUrl" style="width: 100px">
@@ -154,12 +156,14 @@ import {
 } from "@/api/mp/message";
 import Editor from '@/components/Editor/index.vue';
 import WxVideoPlayer from '@/views/mp/components/wx-video-play/main.vue';
+import WxVoicePlayer from '@/views/mp/components/wx-voice-play/main.vue';
 
 export default {
   name: "WxFansMsg",
   components: {
     Editor,
     WxVideoPlayer,
+    WxVoicePlayer
   },
   data() {
     return {

+ 12 - 0
yudao-ui-admin/yarn.lock

@@ -2327,6 +2327,18 @@
   dependencies:
     "tweetnacl" "^0.14.3"
 
+"benz-amr-recorder@^1.1.5":
+  "integrity" "sha512-NepctcNTsZHK8NxBb5uKO5p8S+xkbm+vD6GLSkCYdJeEsriexvgumLHpDkanX4QJBcLRMVtg16buWMs+gUPB3g=="
+  "resolved" "https://registry.npmmirror.com/benz-amr-recorder/-/benz-amr-recorder-1.1.5.tgz"
+  "version" "1.1.5"
+  dependencies:
+    "benz-recorderjs" "^1.0.5"
+
+"benz-recorderjs@^1.0.5":
+  "integrity" "sha512-EwedOQo9KLti7HxDi/eZY51PSRbAXnOdEZmLvJ6ro3QQSoF9Y3AXBt57MIllGvVz5vtFYMeikG+GD7qTm3+p9w=="
+  "resolved" "https://registry.npmmirror.com/benz-recorderjs/-/benz-recorderjs-1.0.5.tgz"
+  "version" "1.0.5"
+
 "bfj@^6.1.1":
   "integrity" "sha512-BmBJa4Lip6BPRINSZ0BPEIfB1wUY/9rwbwvIHQA1KjX9om29B6id0wnWXq7m3bn5JrUVjeOTnVuhPT1FiHwPGw=="
   "resolved" "https://registry.npmmirror.com/bfj/-/bfj-6.1.2.tgz"