voiceBroadcast.vue 10.0 KB

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