monitor.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586
  1. <!-- 查看监控 -->
  2. <template>
  3. <view id="monitor">
  4. <view class="name-box">{{itemData.name}}</view>
  5. <view class="address-box">{{itemData.address}}</view>
  6. <view class="max-viode-box">
  7. <video
  8. v-for="(item,index) in urlList" :key="index" :id="urlList.id" :src="item.url"
  9. :poster="videoCover"
  10. :custom-cache="false" :autoplay="true" :controls="true"
  11. :enable-danmu="false" :muted="true" :show-fullscreen-btn="true"
  12. :show-center-play-btn="false" :show-play-btn="false"
  13. @error="videoErrorCallback" >
  14. </video>
  15. </view>
  16. <view class="gb-box">
  17. <view class="radio-box">
  18. <view class="radio-title">播报方式</view>
  19. <radio-group @change="radioChange" class="radio-max-button">
  20. <label class="radio-button" v-for="(item, index) in items" :key="item.value">
  21. <radio :value="item.value" color="#007AFF" :checked="item.value == current" />{{item.name}}
  22. </label>
  23. </radio-group>
  24. </view>
  25. <view class="input-box" v-if="current == 1">
  26. <view class="textarea-title">广播内容</view>
  27. <textarea placeholder="请输入广播内容" v-model="text" maxlength="100"></textarea>
  28. <view class="input-button" @click="voiceButton">确定</view>
  29. </view>
  30. <view class="device-box" v-if="current == 2">
  31. <view class="device-title">设备</view>
  32. <view class="device-for-max-box">
  33. <view @click="trumpetClick(index)"
  34. class="trumpet-for-box" :class="item.type?'trumpet-color-a':'trumpet-color-b'"
  35. v-for="(item,index) in trumpetList" :key="index">
  36. <view class="trumpet-for-min-box">
  37. <img src="@/images/icon_sskz_zc.png" v-if="!item.type">
  38. <img src="@/images/icon_sskz_xz.png" v-if="item.type">
  39. <view>{{item.name}}</view>
  40. </view>
  41. </view>
  42. </view>
  43. <view class="broadcast_m">
  44. <view class="broadcast_m_t" :class="liveType?'broadcast_m_t_back_a':'broadcast_m_t_back_b'" @longpress.stop="recordButton" @touchmove.stop="cancelButton" @touchend.stop="sendButton">
  45. <!-- <img src="@/images/icon_sskz_azsh.png" v-if="!liveType">
  46. <img src="@/images/icon_sskz_skfs.png" v-if="liveType"> -->
  47. <!-- <label class="slip_color" v-if="!liveType">按住说话</label>
  48. <label class="press_color" v-if="liveType">松开发送</label> -->
  49. {{liveType?'松开发送':'按住说话'}}
  50. </view>
  51. <view class="broadcast_m_b" v-if="!liveType">按住说话,录入广播内容</view>
  52. <view class="broadcast_m_b" v-if="liveType">松开发送,向上滑动取消发送</view>
  53. </view>
  54. </view>
  55. </view>
  56. </view>
  57. </template>
  58. <script>
  59. import { config } from '@/api/request/config.js'
  60. import { getChannels,wenchangGetStartList,nanhuGetStartList ,jndxGetStartList,getDeviceListBySub ,textParseUrlIps ,voice } from '@/api/index.js'
  61. export default {
  62. data() {
  63. return {
  64. videoCover:uni.getStorageSync('videoCover'),
  65. itemData:{},
  66. urlList:[],
  67. deptId:"",
  68. text:"",
  69. type:"",
  70. items:[
  71. {
  72. name:"文字",
  73. value:"1"
  74. },
  75. {
  76. name:"音频",
  77. value:"2"
  78. },
  79. ],
  80. current:1,
  81. text:"",
  82. trumpetList:[],
  83. //滑动记录
  84. startPoint:{},
  85. //广播相关
  86. liveType:false,
  87. sendLock: true, //发送锁,当为true时上锁,false时解锁发送
  88. recorderManager : wx.getRecorderManager(),
  89. }
  90. },
  91. onLoad(option) {
  92. this.itemData = JSON.parse(decodeURIComponent(option.item));
  93. this.deptId = JSON.parse(decodeURIComponent(option.deptId));
  94. console.log(this.itemData)
  95. console.log(this.deptId)
  96. this.jndxGetStartList();//暨南大学调用摄像头列表
  97. this.getDeviceListBySub();
  98. // this.getChannels();
  99. },
  100. onShow(){
  101. // wx.getSystemInfo({
  102. // success (res) {
  103. // console.log("res",res)
  104. // }
  105. // })
  106. },
  107. methods: {
  108. voiceButton(){
  109. let self = this;
  110. if(!this.text){
  111. uni.showToast({
  112. title: '请输入广播内容',
  113. icon:"none",
  114. mask:true,
  115. duration: 2000
  116. });
  117. return
  118. }
  119. uni.showModal({
  120. // title: '确认要退出吗?',
  121. content: '确认播放吗?',
  122. cancelColor:"#999",
  123. confirmColor:"#0183FA",
  124. success: function (res) {
  125. if (res.confirm) {
  126. self.voice();
  127. console.log('用户点击确定');
  128. } else if (res.cancel) {
  129. console.log('用户点击取消');
  130. }
  131. }
  132. });
  133. },
  134. //播放
  135. async voice(){
  136. let obj = {
  137. txt:this.text,
  138. type:'1'
  139. };
  140. const {data} = await voice(this.itemData.id,obj);
  141. if(data.code == 200){
  142. this.text = "";
  143. uni.showToast({
  144. title: '播放成功',
  145. icon:"none",
  146. mask:true,
  147. duration: 2000
  148. });
  149. }
  150. },
  151. //点击选择喇叭
  152. trumpetClick(index){
  153. this.trumpetList[index].type = !this.trumpetList[index].type
  154. },
  155. //录制
  156. recordButton(e){
  157. console.log("按下")
  158. let self = this;
  159. let num = 0;
  160. for(let i=0;i<self.trumpetList.length;i++){
  161. if(self.trumpetList[i].type){
  162. num++
  163. }
  164. }
  165. if(num == 0){
  166. uni.showToast({
  167. title: '请选择喇叭',
  168. icon:"none",
  169. mask:true,
  170. duration: 2000
  171. });
  172. return
  173. }
  174. this.liveType=true;
  175. console.log('录制',e)
  176. this.startPoint = e.touches[0];//记录长按时开始点信息,后面用于计算上划取消时手指滑动的距离。
  177. const options = {
  178. duration: 10000,
  179. sampleRate: 16000,
  180. numberOfChannels: 1,
  181. encodeBitRate: 48000,
  182. format: 'mp3',
  183. frameSize: 50
  184. }
  185. this.recorderManager.start(options);//开始录音
  186. this.recorderManager.onStart(() => {
  187. console.log('recorder start')
  188. })
  189. this.recorderManager.onError((res) => {
  190. console.log(res);
  191. })
  192. wx.showToast({
  193. title: "正在录音,上划取消发送",
  194. icon: "none",
  195. duration: 60000//先定义个60秒,后面可以手动调用wx.hideToast()隐藏
  196. });
  197. this.sendLock = false;//长按时是不上锁的。
  198. },
  199. //取消
  200. cancelButton(e){
  201. console.log("移动")
  202. let self = this;
  203. let num = 0;
  204. for(let i=0;i<self.trumpetList.length;i++){
  205. if(self.trumpetList[i].type){
  206. num++
  207. }
  208. }
  209. if(num == 0){
  210. return
  211. }
  212. this.liveType=false;
  213. console.log('取消',e)
  214. let moveLenght = e.touches[e.touches.length - 1].clientY - this.startPoint.clientY; //移动距离
  215. if (Math.abs(moveLenght) > 50) {
  216. wx.showToast({
  217. title: "松开手指,取消发送",
  218. icon: "none",
  219. duration: 60000
  220. });
  221. this.sendLock = true;//触发了上滑取消发送,上锁
  222. } else {
  223. wx.showToast({
  224. title: "正在录音,上划取消发送",
  225. icon: "none",
  226. duration: 60000
  227. });
  228. this.sendLock = false;//上划距离不足,依然可以发送,不上锁
  229. }
  230. },
  231. //发送
  232. sendButton(e){
  233. console.log("松开")
  234. let self = this;
  235. let num = 0;
  236. for(let i=0;i<self.trumpetList.length;i++){
  237. if(self.trumpetList[i].type){
  238. num++
  239. }
  240. }
  241. if(num == 0){
  242. return
  243. }
  244. this.liveType=false;
  245. console.log('发送',e)
  246. wx.hideToast();//结束录音、隐藏Toast提示框
  247. this.recorderManager.stop();//结束录音
  248. this.recorderManager.onStop((res) => {
  249. if(!this.sendLock){
  250. console.log('1', this.recorderManager)
  251. this.uploadImg(res.tempFilePath);
  252. }
  253. console.log('停止录音', res.tempFilePath)
  254. console.log("sendLock",this.sendLock);
  255. })
  256. },
  257. //上传MP3
  258. async uploadImg(tempFilePaths){
  259. var self = this;
  260. uni.uploadFile({
  261. url: config.base_url+'/file/upload', //仅为示例,非真实的接口地址
  262. header:{'Authorization':uni.getStorageSync('token')},
  263. filePath: tempFilePaths,
  264. name: 'file',
  265. formData: {
  266. 'user': 'test'
  267. },
  268. success: (uploadFileRes) => {
  269. let res = JSON.parse(uploadFileRes.data);
  270. if(res.code == 200){
  271. console.log("上传成功",res)
  272. self.textParseUrlIps(config.base_url+ '/' +res.data.url);
  273. // self.newData.imgList.push(res.data.url);
  274. }else{
  275. uni.showToast({
  276. title: res.msg,
  277. icon:"none",
  278. mask:true,
  279. duration: 2000
  280. });
  281. }
  282. },
  283. fail: err => {
  284. uni.hideLoading()
  285. },
  286. complete: () => {
  287. }
  288. });
  289. },
  290. //发送语音
  291. async textParseUrlIps(text){
  292. let self = this;
  293. let newList = [];
  294. for(let i=0;i<self.trumpetList.length;i++){
  295. if(self.trumpetList[i].type){
  296. let obj = {
  297. sn:self.trumpetList[i].deviceSn,
  298. port:self.trumpetList[i].port,
  299. deviceIp:self.trumpetList[i].deviceIp,
  300. type:"",
  301. name:"",
  302. speed:"",
  303. params:{
  304. tid:"",
  305. vol:"",
  306. urls:[]
  307. }
  308. };
  309. newList.push(obj);
  310. }
  311. }
  312. const {data} = await textParseUrlIps(newList,text)
  313. if(data.code == 200){
  314. uni.showToast({
  315. title: '发送成功',
  316. icon:"none",
  317. mask:true,
  318. duration: 2000
  319. });
  320. }
  321. },
  322. radioChange(e){
  323. this.current = e.detail.value;
  324. },
  325. //获取喇叭列表
  326. async getDeviceListBySub(){
  327. let obj = {
  328. floorId:this.itemData.floorId,
  329. subId:this.itemData.id,
  330. page:1,
  331. pageSize:100
  332. }
  333. const {data} = await getDeviceListBySub(obj);
  334. if(data.code == 200){
  335. for(let i=0;i<data.data.length;i++){
  336. data.data[i].type = false;
  337. }
  338. this.$set(this,'trumpetList',data.data)
  339. // console.log(data);
  340. }
  341. },
  342. //查询摄像头地址(暨南大学)
  343. async jndxGetStartList(){
  344. let obj = {
  345. page:'1',
  346. count:'2',
  347. deviceIds:this.itemData.hardwareNUM
  348. };
  349. const {data} = await jndxGetStartList(obj);
  350. if(data.code == 200){
  351. let list = [];
  352. for(let i=0;i<data.data.length;i++){
  353. let text = 'https://lab.sxitdlc.com/jinandaxue/stream/';
  354. let url = data.data[i].result.body.data.hls;
  355. url = url.split("rtp/");
  356. let newUrl = text+'rtp/'+url[1];
  357. let obj = {
  358. id:data.data[i].result.body.data.deviceID,
  359. url:newUrl,
  360. }
  361. list.push(obj)
  362. console.log("obj",obj)
  363. }
  364. this.urlList = list;
  365. }
  366. },
  367. //查询摄像头地址
  368. async getChannels(){
  369. let obj = {
  370. page:'1',
  371. count:'2',
  372. deviceIds:this.itemData.hardwareNUM
  373. };
  374. const {data} = await getChannels(obj);
  375. if(data.code == 200){
  376. let list = [];
  377. for(let i=0;i<data.data.length;i++){
  378. let obj = {
  379. id:data.data[i].result.body.data.deviceID,
  380. url:data.data[i].result.body.data.fmp4,
  381. }
  382. list.push(obj)
  383. }
  384. this.urlList = list;
  385. }
  386. },
  387. videoErrorCallback(e){
  388. console.log("播放失败",e);
  389. // uni.showToast({
  390. // title: '视频播放失败',
  391. // icon:"none",
  392. // mask:true,
  393. // duration: 3000
  394. // });
  395. },
  396. }
  397. }
  398. </script>
  399. <style lang="stylus" scoped>
  400. #monitor{
  401. padding:10rpx 20rpx;
  402. background #fff
  403. .name-box{
  404. line-height:50rpx;
  405. font-size:30rpx;
  406. color:#333;
  407. }
  408. .address-box{
  409. line-height:50rpx;
  410. font-size:28rpx;
  411. color:#666;
  412. margin-bottom:20rpx;
  413. }
  414. .max-viode-box{
  415. height: 720rpx;
  416. overflow-y scroll;
  417. video{
  418. width:710rpx;
  419. height:355rpx;
  420. }
  421. }
  422. .gb-box{
  423. margin-top:20rpx;
  424. .radio-box{
  425. display flex;
  426. font-size:26rpx;
  427. .radio-title{
  428. font-size:28rpx;
  429. margin:20rpx 0 20rpx 20rpx;
  430. line-height:40rpx;
  431. }
  432. .radio-max-button{
  433. display flex
  434. margin:0 100rpx;
  435. height:40rpx;
  436. margin-top:20rpx;
  437. .radio-button{
  438. flex:1;
  439. margin-left:75rpx;
  440. font-size:26rpx;
  441. }
  442. }
  443. }
  444. .input-box{
  445. .textarea-title{
  446. font-size:28rpx;
  447. margin:20rpx 0 20rpx 20rpx;
  448. line-height:40rpx;
  449. }
  450. textarea{
  451. width:630rpx;
  452. height:200rpx;
  453. margin:20rpx 20rpx 0;
  454. font-size:26rpx;
  455. border: 1rpx solid #E0E0E0;
  456. border-radius:10rpx;
  457. padding:20rpx;
  458. }
  459. .input-button{
  460. margin:20rpx auto;
  461. width:600rpx;
  462. height:80rpx;
  463. line-height:80rpx;
  464. color:#fff;
  465. background #007AFF;
  466. text-align center;
  467. border-radius:20rpx;
  468. }
  469. }
  470. .device-box{
  471. margin-bottom:50rpx;
  472. .device-title{
  473. font-size:28rpx;
  474. margin:20rpx 0 20rpx 20rpx;
  475. line-height:40rpx;
  476. }
  477. .device-for-max-box{
  478. margin-top :22rpx;
  479. .trumpet-for-box{
  480. display:inline-block !important;
  481. width:200rpx;
  482. height:60rpx;
  483. line-height:60rpx;
  484. font-size:24rpx;
  485. text-align center;
  486. cursor: pointer;
  487. overflow: hidden;
  488. border: 1rpx solid #E0E0E0;
  489. border-radius:10rpx;
  490. color: #E0E0E0;
  491. justify-content :center;
  492. // margin-right :30rpx;
  493. margin: 10rpx 16rpx;
  494. .trumpet-for-min-box{
  495. display flex
  496. >img{
  497. display:inline-block !important;
  498. width:36rpx;
  499. height:34rpx;
  500. margin :13rpx 20rpx 0 25rpx;
  501. }
  502. view{
  503. display:inline-block !important;
  504. }
  505. }
  506. }
  507. .trumpet-color-a{
  508. border:1px solid #0183FA;
  509. color:#0183FA;
  510. }
  511. .trumpet-color-b{
  512. border:1px solid #CCCCCC;
  513. color:#999;
  514. }
  515. }
  516. .broadcast_m{
  517. width :100%;
  518. .broadcast_m_t{
  519. width: 142rpx;
  520. height: 142rpx;
  521. margin :30rpx 0 0 258rpx;
  522. margin:30rpx auto 0;
  523. position :relative;
  524. font-size: 24rpx;
  525. font-family: PingFang SC;
  526. font-weight: 500;
  527. line-height: 170rpx;
  528. text-align center
  529. >img{
  530. width: 142rpx;
  531. height: 142rpx;
  532. position :absolute;
  533. }
  534. >label{
  535. width :100%;
  536. font-size: 24rpx;
  537. font-family: PingFang SC;
  538. font-weight: 500;
  539. color: #0183FA;
  540. line-height: 24rpx;
  541. display :inline-block;
  542. text-align :center;
  543. position :absolute;
  544. top:76rpx;
  545. }
  546. /* 按下 */
  547. .press_color{
  548. color: #FFFFFF;
  549. }
  550. /* 松开 */
  551. .slip_color{
  552. color: #0183FA;
  553. }
  554. }
  555. .broadcast_m_t_back_a{
  556. background:url(@/images/icon_sskz_skfs.png);
  557. background-size 100%
  558. color: #FFFFFF;
  559. }
  560. .broadcast_m_t_back_b{
  561. background:url(@/images/icon_sskz_azsh.png);
  562. background-size 100%
  563. color: #0183FA;
  564. }
  565. .broadcast_m_b{
  566. font-size: 24rpx;
  567. font-family: PingFang SC;
  568. font-weight: 500;
  569. color: #999999;
  570. line-height: 24rpx;
  571. text-align center;
  572. margin-top :14rpx;
  573. }
  574. }
  575. }
  576. }
  577. }
  578. </style>