videoMonitoring.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579
  1. <!-- 视频监控-->
  2. <template>
  3. <view class="earlyWarning">
  4. <scroll-view scroll-x @scrolltolower="scrollGet" class="header">
  5. <view class="tabTitle_tow">
  6. <view class="tabTitle_tow_li" @tap="tabClickTow(item,index)" :key="index"
  7. v-for="(item,index) in videoHardwareList">
  8. <view :class="{on:curTabTow==index}" class="tabTitle_tow_text">{{item.name}}</view>
  9. <view :class="{on:curTabTow==index}" class="tabTitle_tow_across"></view>
  10. </view>
  11. </view>
  12. </scroll-view>
  13. <scroll-view scroll-y @scrolltolower="scrollGet" class="info-max-box">
  14. <!-- 视频部分 -->
  15. <view class="video-max-box">
  16. <video v-for="(item,index) in urlList" :key="index" ref="videoRef" :src="item.url" :poster="videoCover"
  17. :custom-cache='false' :autoplay="true" :controls="true" :enable-danmu="false" :muted="true"
  18. :show-fullscreen-btn="true" :show-center-play-btn="false" :show-play-btn="false" @error="videoErrorCallback">
  19. </video>
  20. </view>
  21. <view class="broadcast">
  22. <view class="broadcast_t">语音广播<label>选择喇叭位置</label></view>
  23. <!-- 按钮部分 -->
  24. <view class="trumpet-max-box">
  25. <view @click="trumpetClick(index)" class="trumpet-for-box"
  26. :class="item.type?'trumpet-color-a':'trumpet-color-b'" v-for="(item,index) in trumpetList" :key="index">
  27. <img :src="imagesUrl('commonality/icon_sskz_zc.png')" v-if="!item.type">
  28. <img :src="imagesUrl('commonality/icon_sskz_xz.png')" v-if="item.type">
  29. {{item.name}}
  30. </view>
  31. </view>
  32. <view class="broadcast_m">
  33. <view class="broadcast_m_t" :class="liveType?'broadcast_m_t_back_a':'broadcast_m_t_back_b'"
  34. @longpress.stop="recordButton" @touchmove.stop="cancelButton" @touchend.stop="sendButton">
  35. {{liveType?'松开发送':'按住说话'}}
  36. </view>
  37. <view class="broadcast_m_b" v-if="!liveType">按住说话,录入广播内容</view>
  38. <view class="broadcast_m_b" v-if="liveType">松开发送,向上滑动取消发送</view>
  39. </view>
  40. </view>
  41. </scroll-view>
  42. </view>
  43. </template>
  44. <script>
  45. import {
  46. config
  47. } from '@/api/request/config.js'
  48. import {
  49. laboratoryInfo,
  50. getCameraByFloor,
  51. GetStartList,
  52. getDeviceListBySub,
  53. sparseHardwareList,
  54. textParseUrlIps
  55. } from '@/api/apiDemo/index.js'
  56. import {
  57. iotAppSpeakerFindHorn,
  58. iotAppSpeakerPlayVoice
  59. } from '@/pages_manage/api/index.js'
  60. export default {
  61. name: "rectifyList",
  62. components: {
  63. },
  64. data() {
  65. return {
  66. videoCover: uni.getStorageSync('videoCover'),
  67. tabTextTow: [{
  68. name: 'RFID摄像头'
  69. }, {
  70. name: '房间内监控1'
  71. }, {
  72. name: '房间内监控2'
  73. }, {
  74. name: '房间内监控3'
  75. }, {
  76. name: '房间内监控3'
  77. }, {
  78. name: '房间内监控3'
  79. }, ],
  80. curTabTow: 0,
  81. //视频数据
  82. urlList: [{}],
  83. videoHardwareNUM: [], //实验室和楼道摄像头编码
  84. trumpetList: [],
  85. //广播相关
  86. liveType: false,
  87. sendLock: true, //发送锁,当为true时上锁,false时解锁发送
  88. recorderManager: wx.getRecorderManager(),
  89. videoHardwareList: [], //获取所有摄像头编码
  90. hardwareList: [], //硬件列表
  91. subId: '',
  92. floorId: '',
  93. }
  94. },
  95. onLoad(option) {
  96. if (option.subId || option.floorId) {
  97. this.subId = option.subId
  98. this.floorId = option.floorId
  99. }
  100. },
  101. onShow() {
  102. },
  103. mounted() {
  104. this.getDeviceList() //喇叭接口
  105. this.GetVideoList(); //摄像头接口
  106. },
  107. methods: {
  108. //顶部tab点击
  109. tabClickTow(item, index) {
  110. this.curTabTow = index;
  111. console.log(item)
  112. this.GetStartList(item)
  113. },
  114. //滚动事件
  115. scrollGet() {
  116. let self = this;
  117. },
  118. videoErrorCallback(e) {
  119. console.log("播放失败", e);
  120. },
  121. //查询摄像头地址
  122. async GetVideoList() {
  123. let obj = {
  124. page: '1',
  125. count: '10',
  126. deviceIds: this.itemData.hardwareNUM
  127. };
  128. const {
  129. data
  130. } = await GetStartList(obj);
  131. if (data.code == 200) {
  132. let list = [];
  133. for (let i = 0; i < data.data.length; i++) {
  134. let text = uni.getStorageSync('cameraUrl');
  135. let url = data.data[i].hls;
  136. url = url.split("rtp/");
  137. let newUrl = text + 'rtp/' + url[1];
  138. let obj = {
  139. id: data.data[i].deviceID,
  140. url: newUrl,
  141. }
  142. list.push(obj)
  143. console.log("obj", obj)
  144. }
  145. this.urlList = list;
  146. }
  147. },
  148. //获取喇叭列表
  149. async getDeviceList() {
  150. let obj = {
  151. subId: this.subId,
  152. floorId: this.floorId,
  153. };
  154. const {
  155. data
  156. } = await iotAppSpeakerFindHorn(obj)
  157. if (data.code == 200) {
  158. for (let i = 0; i < data.data.length; i++) {
  159. data.data[i].type = false;
  160. }
  161. this.$set(this, 'trumpetList', data.data)
  162. }
  163. },
  164. //点击选择喇叭
  165. trumpetClick(index) {
  166. this.trumpetList[index].type = !this.trumpetList[index].type
  167. },
  168. //录制
  169. recordButton(e) {
  170. console.log("按下")
  171. let self = this;
  172. let num = 0;
  173. for (let i = 0; i < self.trumpetList.length; i++) {
  174. if (self.trumpetList[i].type) {
  175. num++
  176. }
  177. }
  178. if (num == 0) {
  179. uni.showToast({
  180. title: '请选择喇叭',
  181. icon: "none",
  182. mask: true,
  183. duration: 2000
  184. });
  185. return
  186. }
  187. this.liveType = true;
  188. console.log('录制', e)
  189. this.startPoint = e.touches[0]; //记录长按时开始点信息,后面用于计算上划取消时手指滑动的距离。
  190. const options = {
  191. duration: 10000,
  192. sampleRate: 16000,
  193. numberOfChannels: 1,
  194. encodeBitRate: 48000,
  195. format: 'mp3',
  196. frameSize: 50
  197. }
  198. this.recorderManager.start(options); //开始录音
  199. this.recorderManager.onStart(() => {
  200. console.log('recorder start')
  201. })
  202. this.recorderManager.onError((res) => {
  203. console.log(res);
  204. })
  205. wx.showToast({
  206. title: "正在录音,上划取消发送",
  207. icon: "none",
  208. duration: 60000 //先定义个60秒,后面可以手动调用wx.hideToast()隐藏
  209. });
  210. this.sendLock = false; //长按时是不上锁的。
  211. },
  212. //取消
  213. cancelButton(e) {
  214. console.log("移动")
  215. let self = this;
  216. let num = 0;
  217. for (let i = 0; i < self.trumpetList.length; i++) {
  218. if (self.trumpetList[i].type) {
  219. num++
  220. }
  221. }
  222. if (num == 0) {
  223. return
  224. }
  225. this.liveType = false;
  226. console.log('取消', e)
  227. let moveLenght = e.touches[e.touches.length - 1].clientY - this.startPoint.clientY; //移动距离
  228. if (Math.abs(moveLenght) > 50) {
  229. wx.showToast({
  230. title: "松开手指,取消发送",
  231. icon: "none",
  232. duration: 60000
  233. });
  234. this.sendLock = true; //触发了上滑取消发送,上锁
  235. } else {
  236. wx.showToast({
  237. title: "正在录音,上划取消发送",
  238. icon: "none",
  239. duration: 60000
  240. });
  241. this.sendLock = false; //上划距离不足,依然可以发送,不上锁
  242. }
  243. },
  244. //发送
  245. sendButton(e) {
  246. console.log("松开")
  247. let self = this;
  248. let num = 0;
  249. for (let i = 0; i < self.trumpetList.length; i++) {
  250. if (self.trumpetList[i].type) {
  251. num++
  252. }
  253. }
  254. if (num == 0) {
  255. return
  256. }
  257. this.liveType = false;
  258. console.log('发送', e)
  259. wx.hideToast(); //结束录音、隐藏Toast提示框
  260. this.recorderManager.stop(); //结束录音
  261. this.recorderManager.onStop((res) => {
  262. if (!this.sendLock) {
  263. console.log('1', this.recorderManager)
  264. this.uploadImg(res.tempFilePath);
  265. }
  266. console.log('停止录音', res.tempFilePath)
  267. console.log("sendLock", this.sendLock);
  268. })
  269. },
  270. //上传MP3
  271. async uploadImg(tempFilePaths) {
  272. var self = this;
  273. uni.uploadFile({
  274. url: config.base_url + '/system/file/upload', //仅为示例,非真实的接口地址
  275. header: {
  276. 'Authorization': uni.getStorageSync('token')
  277. },
  278. filePath: tempFilePaths,
  279. name: 'file',
  280. formData: {
  281. 'user': 'test'
  282. },
  283. success: (uploadFileRes) => {
  284. let res = JSON.parse(uploadFileRes.data);
  285. if (res.code == 200) {
  286. console.log("上传成功", res)
  287. let url = 'http://' + uni.getStorageSync('mqttIntranetUrl').split(':')[0] +
  288. '/' + res.data.url
  289. self.iotAppSpeakerPlayVoice(url);
  290. } else {
  291. uni.showToast({
  292. title: res.msg,
  293. icon: "none",
  294. mask: true,
  295. duration: 2000
  296. });
  297. }
  298. },
  299. fail: err => {
  300. uni.hideLoading()
  301. },
  302. complete: () => {}
  303. });
  304. },
  305. //发送语音
  306. async iotAppSpeakerPlayVoice(text) {
  307. let self = this;
  308. let list = [];
  309. for (let i = 0; i < self.trumpetList.length; i++) {
  310. if (self.trumpetList[i].type) {
  311. list.push(self.trumpetList[i].deviceNo)
  312. }
  313. }
  314. let obj = {
  315. deviceNo: list.join(','),
  316. voiceUrls: text,
  317. cycle: 1,
  318. level: 1000,
  319. }
  320. const {
  321. data
  322. } = await iotAppSpeakerPlayVoice(obj)
  323. if (data.code == 200) {
  324. uni.showToast({
  325. title: '发送成功',
  326. icon: "none",
  327. mask: true,
  328. duration: 2000
  329. });
  330. }
  331. },
  332. handleClick(row, doType) {
  333. let self = this;
  334. if (doType == 'detail') { //详情
  335. }
  336. },
  337. }
  338. }
  339. </script>
  340. <style lang="stylus" scoped>
  341. @import '@/api/request/imagesUrl.styl';
  342. .earlyWarning {
  343. height: 100%;
  344. display flex;
  345. padding-bottom: 30rpx;
  346. box-sizing: border-box;
  347. .header {
  348. width: 750rpx;
  349. height: 100rpx;
  350. position: fixed;
  351. top: 0rpx;
  352. z-index: 100;
  353. background: #fff;
  354. .tabTitle_tow {
  355. height: 100rpx;
  356. white-space: nowrap;
  357. display: inline-flex;
  358. .tabTitle_tow_li {
  359. position: relative;
  360. width: 210rpx;
  361. height: 100rpx;
  362. text-align center;
  363. padding-top: 26rpx;
  364. box-sizing: border-box;
  365. .tabTitle_tow_text {
  366. display: inline-block;
  367. font-size: 30rpx;
  368. font-family: PingFang SC;
  369. font-weight: 500;
  370. color: #333333;
  371. line-height: 46rpx;
  372. position: relative;
  373. &.on {
  374. color: #0183FA;
  375. }
  376. }
  377. .tabTitle_tow_across {
  378. width: 50rpx;
  379. height: 4rpx;
  380. background: #0183FA;
  381. border-radius: 2rpx;
  382. margin-left: 33%;
  383. display none;
  384. &.on {
  385. display block;
  386. }
  387. }
  388. }
  389. }
  390. }
  391. .video-max-box {
  392. width: 690rpx;
  393. height: 420rpx;
  394. margin: 116rpx 30rpx 20rpx;
  395. video {
  396. width: 690rpx;
  397. height: 420rpx;
  398. }
  399. }
  400. .broadcast {
  401. width: 690rpx;
  402. background: #FFFFFF;
  403. border-radius: 20rpx;
  404. padding: 22rpx 30rpx 30rpx;
  405. box-sizing: border-box;
  406. margin: 20rpx 30rpx 0;
  407. .broadcast_t {
  408. font-size: 30rpx;
  409. font-family: PingFang SC;
  410. font-weight: 500;
  411. color: #333333;
  412. line-height: 30rpx;
  413. >label {
  414. font-size: 24rpx;
  415. font-family: PingFang SC;
  416. font-weight: 500;
  417. color: #999999;
  418. line-height: 30rpx;
  419. margin-left: 16rpx;
  420. }
  421. }
  422. .trumpet-max-box {
  423. display: flex;
  424. justify-content: flex-start;
  425. margin-top: 22rpx;
  426. flex-wrap: wrap;
  427. .trumpet-for-box {
  428. display: inline-block;
  429. width: auto;
  430. height: 60rpx;
  431. line-height: 60rpx;
  432. font-size: 24rpx;
  433. text-align: center;
  434. cursor: pointer;
  435. overflow: hidden;
  436. border: 1rpx solid #E0E0E0;
  437. border-radius: 10rpx;
  438. color: #E0E0E0;
  439. display: flex;
  440. justify-content: center;
  441. margin-right: 20rpx;
  442. margin-bottom: 10rpx;
  443. padding: 0 12rpx;
  444. box-sizing: border-box;
  445. >img {
  446. width: 36rpx;
  447. height: 34rpx;
  448. margin: 12rpx 20rpx 0 25rpx;
  449. }
  450. }
  451. .trumpet-color-a {
  452. border: 1px solid #0183FA;
  453. color: #0183FA;
  454. }
  455. .trumpet-color-b {
  456. border: 1px solid #CCCCCC;
  457. color: #999;
  458. }
  459. }
  460. .broadcast_m {
  461. width: 100%;
  462. .broadcast_m_t {
  463. width: 142rpx;
  464. height: 142rpx;
  465. margin: 30rpx 0 0 258rpx;
  466. position: relative;
  467. font-size: 24rpx;
  468. font-family: PingFang SC;
  469. font-weight: 500;
  470. line-height: 170rpx;
  471. text-align: center;
  472. >img {
  473. width: 142rpx;
  474. height: 142rpx;
  475. position: absolute;
  476. }
  477. >label {
  478. width: 100%;
  479. font-size: 24rpx;
  480. font-family: PingFang SC;
  481. font-weight: 500;
  482. color: #0183FA;
  483. line-height: 24rpx;
  484. display: inline-block;
  485. text-align: center;
  486. position: absolute;
  487. top: 76rpx;
  488. }
  489. /* 按下 */
  490. .press_color {
  491. color: #FFFFFF;
  492. }
  493. /* 松开 */
  494. .slip_color {
  495. color: #0183FA;
  496. }
  497. }
  498. .broadcast_m_b {
  499. font-size: 24rpx;
  500. font-family: PingFang SC;
  501. font-weight: 500;
  502. color: #999999;
  503. line-height: 24rpx;
  504. text-align: center;
  505. margin-top: 14rpx;
  506. }
  507. .broadcast_m_t_back_a {
  508. background: url($imagesUrl+'commonality/icon_sskz_skfs.png') no-repeat center 0px;
  509. background-size: 100%;
  510. color: #FFFFFF;
  511. }
  512. .broadcast_m_t_back_b {
  513. background: url($imagesUrl+'commonality/icon_sskz_azsh.png') no-repeat center 0px;
  514. background-size: 100%;
  515. color: #0183FA;
  516. }
  517. }
  518. /* 疏散按钮 */
  519. .evacuation-button-box {
  520. width: 650rpx;
  521. height: 100rpx;
  522. background: #0183FA;
  523. color: #fff;
  524. text-align center;
  525. line-height: 100rpx;
  526. font-size: 28rpx;
  527. margin: 88rpx auto 0;
  528. border-radius: 20rpx;
  529. }
  530. }
  531. }
  532. </style>