voiceBroadcast.vue 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  1. <!-- 语音广播 -->
  2. <template>
  3. <view class="voiceBroadcast">
  4. <view class="null-box" @click="backPage()"></view>
  5. <!-- 语音广播-执行疏散 -->
  6. <view class="broadcast">
  7. <view class="broadcast_t">语音广播<label>选择喇叭位置</label></view>
  8. <!-- 按钮部分 -->
  9. <view class="trumpet-max-box">
  10. <view @click="trumpetClick(index)" class="trumpet-for-box"
  11. :class="item.type?'trumpet-color-a':'trumpet-color-b'" v-for="(item,index) in trumpetList"
  12. :key="index">
  13. <img src="@/images/basicsModules/icon_sskz_zc.png" v-if="!item.type">
  14. <img src="@/images/basicsModules/icon_sskz_xz.png" v-if="item.type">
  15. {{item.deviceName}}
  16. </view>
  17. </view>
  18. <view class="broadcast_m">
  19. <view class="broadcast_m_t"
  20. :class="liveType?'broadcast_m_t_back_a':'broadcast_m_t_back_b'"
  21. @longpress.stop="recordButton"
  22. @touchmove.stop="cancelButton"
  23. @touchend.stop="sendButton"
  24. >
  25. {{liveType?'松开发送':'按住说话'}}
  26. </view>
  27. <view class="broadcast_m_b" v-if="!liveType">按住说话,录入广播内容</view>
  28. <view class="broadcast_m_b" v-if="liveType">松开发送,向上滑动取消发送</view>
  29. </view>
  30. </view>
  31. </view>
  32. </template>
  33. <script>
  34. import {iotAppSpeakerFindHorn,iotAppSpeakerPlayVoice} from '@/api/basicsModules/index.js'
  35. import { config } from '@/api/request/config.js'
  36. export default {
  37. name: "voiceBroadcast",
  38. props: {
  39. subjectData: {},
  40. },
  41. data() {
  42. return {
  43. baseUrl:config.base_url,
  44. //喇叭数据
  45. trumpetList: [],
  46. //广播相关
  47. liveType: false,
  48. sendLock: true, //发送锁,当为true时上锁,false时解锁发送
  49. recorderManager: wx.getRecorderManager(),
  50. isEvacuate: true, //疏散按钮控制,当为true时候执行疏散
  51. //滑动记录
  52. startPoint: {},
  53. subId:'',
  54. floorId:'',
  55. }
  56. },
  57. created() {
  58. },
  59. mounted() {
  60. this.$set(this, 'subId',this.subjectData.subId);
  61. this.$set(this, 'floorId',this.subjectData.floorId);
  62. this.iotAppSpeakerFindHorn();
  63. },
  64. methods: {
  65. // 返回按钮
  66. backPage(){
  67. this.$parent.buttonClick('broadcastClose','');
  68. },
  69. //获取喇叭列表
  70. async iotAppSpeakerFindHorn() {
  71. let self=this;
  72. let obj = {
  73. subId: self.subId,
  74. floorId: self.floorId,
  75. };
  76. const {data} = await iotAppSpeakerFindHorn(obj)
  77. if (data.code == 200) {
  78. for (let i = 0; i < data.data.length; i++) {
  79. data.data[i].type = false;
  80. }
  81. this.$set(this, 'trumpetList', data.data)
  82. }
  83. },
  84. //点击选择喇叭
  85. trumpetClick(index) {
  86. this.trumpetList[index].type = !this.trumpetList[index].type;
  87. },
  88. //录制
  89. recordButton(e) {
  90. console.log("按下")
  91. let self = this;
  92. let num = 0;
  93. for (let i = 0; i < self.trumpetList.length; i++) {
  94. if (self.trumpetList[i].type) {
  95. num++
  96. }
  97. }
  98. if (num == 0) {
  99. uni.showToast({
  100. title: '请选择喇叭',
  101. icon: "none",
  102. mask: true,
  103. duration: 2000
  104. });
  105. return
  106. }
  107. this.liveType = true;
  108. console.log('录制', e)
  109. this.startPoint = e.touches[0]; //记录长按时开始点信息,后面用于计算上划取消时手指滑动的距离。
  110. const options = {
  111. duration: 10000,
  112. sampleRate: 16000,
  113. numberOfChannels: 1,
  114. encodeBitRate: 48000,
  115. format: 'mp3',
  116. frameSize: 50
  117. }
  118. this.recorderManager.start(options); //开始录音
  119. this.recorderManager.onStart(() => {
  120. console.log('recorder start')
  121. })
  122. this.recorderManager.onError((res) => {
  123. console.log(res);
  124. })
  125. wx.showToast({
  126. title: "正在录音,上划取消发送",
  127. icon: "none",
  128. duration: 60000 //先定义个60秒,后面可以手动调用wx.hideToast()隐藏
  129. });
  130. this.sendLock = false; //长按时是不上锁的。
  131. },
  132. //取消
  133. cancelButton(e) {
  134. console.log("移动")
  135. let self = this;
  136. let num = 0;
  137. for (let i = 0; i < self.trumpetList.length; i++) {
  138. if (self.trumpetList[i].type) {
  139. num++
  140. }
  141. }
  142. if (num == 0) {
  143. return
  144. }
  145. this.liveType = false;
  146. console.log('取消', e)
  147. let moveLenght = e.touches[e.touches.length - 1].clientY - this.startPoint.clientY; //移动距离
  148. if (Math.abs(moveLenght) > 50) {
  149. wx.showToast({
  150. title: "松开手指,取消发送",
  151. icon: "none",
  152. duration: 60000
  153. });
  154. this.sendLock = true; //触发了上滑取消发送,上锁
  155. } else {
  156. wx.showToast({
  157. title: "正在录音,上划取消发送",
  158. icon: "none",
  159. duration: 60000
  160. });
  161. this.sendLock = false; //上划距离不足,依然可以发送,不上锁
  162. }
  163. },
  164. //发送
  165. sendButton(e) {
  166. console.log("松开")
  167. let self = this;
  168. let num = 0;
  169. for (let i = 0; i < self.trumpetList.length; i++) {
  170. if (self.trumpetList[i].type) {
  171. num++
  172. }
  173. }
  174. if (num == 0) {
  175. return
  176. }
  177. this.liveType = false;
  178. console.log('发送', e)
  179. wx.hideToast(); //结束录音、隐藏Toast提示框
  180. this.recorderManager.stop(); //结束录音
  181. this.recorderManager.onStop((res) => {
  182. if (!this.sendLock) {
  183. console.log('1', this.recorderManager)
  184. this.uploadImg(res.tempFilePath);
  185. }
  186. console.log('停止录音', res.tempFilePath)
  187. console.log("sendLock", this.sendLock);
  188. })
  189. },
  190. //上传MP3
  191. async uploadImg(tempFilePaths) {
  192. var self = this;
  193. uni.uploadFile({
  194. url: config.base_url + '/system/file/upload', //仅为示例,非真实的接口地址
  195. header: {
  196. 'Authorization': uni.getStorageSync('token')
  197. },
  198. filePath: tempFilePaths,
  199. name: 'file',
  200. formData: {
  201. 'user': 'test'
  202. },
  203. success: (uploadFileRes) => {
  204. let res = JSON.parse(uploadFileRes.data);
  205. if (res.code == 200) {
  206. console.log("上传成功", res)
  207. self.iotAppSpeakerPlayVoice(config.base_url + '/' + res.data.url);
  208. } else {
  209. uni.showToast({
  210. title: res.msg,
  211. icon: "none",
  212. mask: true,
  213. duration: 2000
  214. });
  215. }
  216. },
  217. fail: err => {
  218. uni.hideLoading()
  219. },
  220. complete: () => {}
  221. });
  222. },
  223. //发送语音
  224. async iotAppSpeakerPlayVoice(text) {
  225. let self = this;
  226. let list=[];
  227. for (let i = 0; i < self.trumpetList.length; i++) {
  228. if (self.trumpetList[i].type) {
  229. list.push(self.trumpetList[i].deviceNo)
  230. }
  231. }
  232. console.log(text)
  233. let obj={
  234. deviceNo:list.join(','),
  235. voiceUrls:text,
  236. cycle:1,
  237. }
  238. const {data} = await iotAppSpeakerPlayVoice(obj)
  239. if (data.code == 200) {
  240. uni.showToast({
  241. title: '发送成功',
  242. icon: "none",
  243. mask: true,
  244. duration: 2000
  245. });
  246. }
  247. },
  248. },
  249. }
  250. </script>
  251. <style lang="stylus" scoped>
  252. .voiceBroadcast{
  253. height: 100%;
  254. width: 100%;
  255. position: fixed;
  256. top: 0;
  257. display: flex;
  258. flex-direction: column;
  259. z-index: 10;
  260. background: rgba(0, 0, 0, 0.2);
  261. .null-box {
  262. flex: 1;
  263. }
  264. /* 语音广播-执行疏散 */
  265. .broadcast {
  266. width: 100%;
  267. background: #FFFFFF;
  268. border-top-left-radius: 20rpx;
  269. border-top-right-radius: 20rpx;
  270. padding:22rpx 30rpx 30rpx;
  271. box-sizing: border-box;
  272. margin-top: 20rpx;
  273. position: absolute;
  274. bottom: 0;
  275. .broadcast_t {
  276. font-size: 30rpx;
  277. font-family: PingFang SC;
  278. font-weight: 500;
  279. color: #333333;
  280. line-height: 30rpx;
  281. >label {
  282. font-size: 24rpx;
  283. font-family: PingFang SC;
  284. font-weight: 500;
  285. color: #999999;
  286. line-height: 30rpx;
  287. margin-left: 16rpx;
  288. }
  289. }
  290. .trumpet-max-box {
  291. display: flex;
  292. justify-content: flex-start;
  293. margin-top: 22rpx;
  294. flex-wrap: wrap;
  295. .trumpet-for-box {
  296. display: inline-block;
  297. width: auto;
  298. height: 60rpx;
  299. line-height: 60rpx;
  300. font-size: 24rpx;
  301. text-align: center;
  302. cursor: pointer;
  303. overflow: hidden;
  304. border: 1rpx solid #E0E0E0;
  305. border-radius: 10rpx;
  306. color: #E0E0E0;
  307. display: flex;
  308. justify-content: center;
  309. margin-right: 20rpx;
  310. margin-bottom: 10rpx;
  311. padding: 0 12rpx;
  312. box-sizing: border-box;
  313. >img {
  314. width: 36rpx;
  315. height: 34rpx;
  316. margin: 12rpx 20rpx 0 25rpx;
  317. }
  318. }
  319. .trumpet-color-a {
  320. border: 1px solid #0183FA;
  321. color: #0183FA;
  322. }
  323. .trumpet-color-b {
  324. border: 1px solid #CCCCCC;
  325. color: #999;
  326. }
  327. }
  328. .broadcast_m {
  329. width: 100%;
  330. .broadcast_m_t {
  331. width: 142rpx;
  332. height: 142rpx;
  333. margin: 30rpx 0 0 258rpx;
  334. position: relative;
  335. font-size: 24rpx;
  336. font-family: PingFang SC;
  337. font-weight: 500;
  338. line-height: 170rpx;
  339. text-align: center;
  340. >img {
  341. width: 142rpx;
  342. height: 142rpx;
  343. position: absolute;
  344. }
  345. >label {
  346. width: 100%;
  347. font-size: 24rpx;
  348. font-family: PingFang SC;
  349. font-weight: 500;
  350. color: #0183FA;
  351. line-height: 24rpx;
  352. display: inline-block;
  353. text-align: center;
  354. position: absolute;
  355. top: 76rpx;
  356. }
  357. /* 按下 */
  358. .press_color {
  359. color: #FFFFFF;
  360. }
  361. /* 松开 */
  362. .slip_color {
  363. color: #0183FA;
  364. }
  365. }
  366. .broadcast_m_t_back_a {
  367. background: url(@/images/basicsModules/icon_sskz_skfs.png);
  368. background-size: 100%;
  369. color: #FFFFFF;
  370. }
  371. .broadcast_m_t_back_b {
  372. background: url(@/images/basicsModules/icon_sskz_azsh.png);
  373. background-size: 100%;
  374. color: #0183FA;
  375. }
  376. .broadcast_m_b {
  377. font-size: 24rpx;
  378. font-family: PingFang SC;
  379. font-weight: 500;
  380. color: #999999;
  381. line-height: 24rpx;
  382. text-align: center;
  383. margin-top: 14rpx;
  384. }
  385. }
  386. /* 疏散按钮 */
  387. .evacuation-button-box {
  388. width: 650rpx;
  389. height: 100rpx;
  390. background: #0183FA;
  391. color: #fff;
  392. text-align center;
  393. line-height: 100rpx;
  394. font-size: 28rpx;
  395. margin: 88rpx auto 0;
  396. border-radius: 20rpx;
  397. }
  398. }
  399. }
  400. </style>