voiceBroadcast.vue 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  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. this.liveType = false;
  148. console.log('取消', e)
  149. let moveLenght = e.touches[e.touches.length - 1].clientY - this.startPoint.clientY; //移动距离
  150. if (Math.abs(moveLenght) > 50) {
  151. wx.showToast({
  152. title: "松开手指,取消发送",
  153. icon: "none",
  154. duration: 60000
  155. });
  156. this.sendLock = true; //触发了上滑取消发送,上锁
  157. } else {
  158. wx.showToast({
  159. title: "正在录音,上划取消发送",
  160. icon: "none",
  161. duration: 60000
  162. });
  163. this.sendLock = false; //上划距离不足,依然可以发送,不上锁
  164. }
  165. },
  166. //发送
  167. sendButton(e) {
  168. console.log("松开")
  169. let self = this;
  170. let num = 0;
  171. for (let i = 0; i < self.trumpetList.length; i++) {
  172. if (self.trumpetList[i].type) {
  173. num++
  174. }
  175. }
  176. if (num == 0) {
  177. return
  178. }
  179. this.liveType = false;
  180. console.log('发送', e)
  181. wx.hideToast(); //结束录音、隐藏Toast提示框
  182. this.recorderManager.stop(); //结束录音
  183. this.recorderManager.onStop((res) => {
  184. if (!this.sendLock) {
  185. console.log('1', this.recorderManager)
  186. this.uploadImg(res.tempFilePath);
  187. }
  188. console.log('停止录音', res.tempFilePath)
  189. console.log("sendLock", this.sendLock);
  190. })
  191. },
  192. //上传MP3
  193. async uploadImg(tempFilePaths) {
  194. var self = this;
  195. uni.uploadFile({
  196. url: config.base_url + '/system/file/upload', //仅为示例,非真实的接口地址
  197. header: {
  198. 'Authorization': uni.getStorageSync('token')
  199. },
  200. filePath: tempFilePaths,
  201. name: 'file',
  202. formData: {
  203. 'user': 'test'
  204. },
  205. success: (uploadFileRes) => {
  206. let res = JSON.parse(uploadFileRes.data);
  207. if (res.code == 200) {
  208. console.log("上传成功", res)
  209. let url = 'http://' + uni.getStorageSync('mqttIntranetUrl').split(':')[0] + '/' + res.data.url
  210. self.iotAppSpeakerPlayVoice(url);
  211. } else {
  212. uni.showToast({
  213. title: res.msg,
  214. icon: "none",
  215. mask: true,
  216. duration: 2000
  217. });
  218. }
  219. },
  220. fail: err => {
  221. uni.hideLoading()
  222. },
  223. complete: () => {}
  224. });
  225. },
  226. //发送语音
  227. async iotAppSpeakerPlayVoice(text) {
  228. let self = this;
  229. let list = [];
  230. for (let i = 0; i < self.trumpetList.length; i++) {
  231. if (self.trumpetList[i].type) {
  232. list.push(self.trumpetList[i].deviceNo)
  233. }
  234. }
  235. let obj = {
  236. deviceNo: list.join(','),
  237. voiceUrls: text,
  238. cycle: 1,
  239. level: 1000,
  240. }
  241. const {
  242. data
  243. } = await iotAppSpeakerPlayVoice(obj)
  244. if (data.code == 200) {
  245. uni.showToast({
  246. title: '发送成功',
  247. icon: "none",
  248. mask: true,
  249. duration: 2000
  250. });
  251. }
  252. },
  253. },
  254. }
  255. </script>
  256. <style lang="stylus" scoped>
  257. @import '@/api/request/imagesUrl.styl';
  258. .voiceBroadcast {
  259. height: 100%;
  260. width: 100%;
  261. position: fixed;
  262. top: 0;
  263. display: flex;
  264. flex-direction: column;
  265. z-index: 10;
  266. background: rgba(0, 0, 0, 0.2);
  267. .null-box {
  268. flex: 1;
  269. }
  270. /* 语音广播-执行疏散 */
  271. .broadcast {
  272. width: 100%;
  273. background: #FFFFFF;
  274. border-top-left-radius: 20rpx;
  275. border-top-right-radius: 20rpx;
  276. padding: 22rpx 30rpx 30rpx;
  277. box-sizing: border-box;
  278. margin-top: 20rpx;
  279. position: absolute;
  280. bottom: 0;
  281. .broadcast_t {
  282. font-size: 30rpx;
  283. font-family: PingFang SC;
  284. font-weight: 500;
  285. color: #333333;
  286. line-height: 30rpx;
  287. >label {
  288. font-size: 24rpx;
  289. font-family: PingFang SC;
  290. font-weight: 500;
  291. color: #999999;
  292. line-height: 30rpx;
  293. margin-left: 16rpx;
  294. }
  295. }
  296. .trumpet-max-box {
  297. display: flex;
  298. justify-content: flex-start;
  299. margin-top: 22rpx;
  300. flex-wrap: wrap;
  301. .trumpet-for-box {
  302. display: inline-block;
  303. width: auto;
  304. height: 60rpx;
  305. line-height: 60rpx;
  306. font-size: 24rpx;
  307. text-align: center;
  308. cursor: pointer;
  309. overflow: hidden;
  310. border: 1rpx solid #E0E0E0;
  311. border-radius: 10rpx;
  312. color: #E0E0E0;
  313. display: flex;
  314. justify-content: center;
  315. margin-right: 20rpx;
  316. margin-bottom: 10rpx;
  317. padding: 0 12rpx;
  318. box-sizing: border-box;
  319. >img {
  320. width: 36rpx;
  321. height: 34rpx;
  322. margin: 12rpx 20rpx 0 25rpx;
  323. }
  324. }
  325. .trumpet-color-a {
  326. border: 1px solid #0183FA;
  327. color: #0183FA;
  328. }
  329. .trumpet-color-b {
  330. border: 1px solid #CCCCCC;
  331. color: #999;
  332. }
  333. }
  334. .broadcast_m {
  335. width: 100%;
  336. .broadcast_m_t {
  337. width: 142rpx;
  338. height: 142rpx;
  339. margin: 30rpx 0 0 258rpx;
  340. position: relative;
  341. font-size: 24rpx;
  342. font-family: PingFang SC;
  343. font-weight: 500;
  344. line-height: 170rpx;
  345. text-align: center;
  346. >img {
  347. width: 142rpx;
  348. height: 142rpx;
  349. position: absolute;
  350. }
  351. >label {
  352. width: 100%;
  353. font-size: 24rpx;
  354. font-family: PingFang SC;
  355. font-weight: 500;
  356. color: #0183FA;
  357. line-height: 24rpx;
  358. display: inline-block;
  359. text-align: center;
  360. position: absolute;
  361. top: 76rpx;
  362. }
  363. /* 按下 */
  364. .press_color {
  365. color: #FFFFFF;
  366. }
  367. /* 松开 */
  368. .slip_color {
  369. color: #0183FA;
  370. }
  371. }
  372. .broadcast_m_b {
  373. font-size: 24rpx;
  374. font-family: PingFang SC;
  375. font-weight: 500;
  376. color: #999999;
  377. line-height: 24rpx;
  378. text-align: center;
  379. margin-top: 14rpx;
  380. }
  381. .broadcast_m_t_back_a {
  382. background: url($imagesUrl+'commonality/icon_sskz_skfs.png') no-repeat center 0px;
  383. background-size: 100%;
  384. color: #FFFFFF;
  385. }
  386. .broadcast_m_t_back_b {
  387. background: url($imagesUrl+'commonality/icon_sskz_azsh.png') no-repeat center 0px;
  388. background-size: 100%;
  389. color: #0183FA;
  390. }
  391. }
  392. /* 疏散按钮 */
  393. .evacuation-button-box {
  394. width: 650rpx;
  395. height: 100rpx;
  396. background: #0183FA;
  397. color: #fff;
  398. text-align center;
  399. line-height: 100rpx;
  400. font-size: 28rpx;
  401. margin: 88rpx auto 0;
  402. border-radius: 20rpx;
  403. }
  404. }
  405. }
  406. </style>