right.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442
  1. <template>
  2. <div>
  3. <base-panel-right>
  4. <div class="safety-inspection-right-container">
  5. <base-header title="一键巡河"></base-header>
  6. <div class="card-item">
  7. <div class="tools-panel">
  8. <div class="btn add-btn" @click="addTask"><i class="el-icon-plus"></i>&nbsp;新建任务</div>
  9. <div class="input-bg">
  10. <el-input v-model="securityPatrolName" size="mini" placeholder="搜索任务名称" suffix-icon="el-icon-search" @change="searchByName"></el-input>
  11. </div>
  12. </div>
  13. <div class="task-list-container">
  14. <ul class="task-table-list">
  15. <li class="task-table-list-title">
  16. <span>巡查任务</span>
  17. <span>巡查点位数</span>
  18. <span>操作</span>
  19. </li>
  20. <div class="item-area">
  21. <li class="task-table-list-item" v-for="item in taskListData" :key="item.id">
  22. <span>{{ item.securityPatrolName }}</span>
  23. <span>{{ item.countPatrolPoints }}</span>
  24. <div style="display: flex; justify-content: center">
  25. <span style="color: #498ee3; cursor: pointer" @click="toXc(item)">巡查</span>
  26. <span style="color: #ff6a6c; cursor: pointer" @click="handleDelete(item)">删除</span>
  27. </div>
  28. </li>
  29. </div>
  30. </ul>
  31. </div>
  32. </div>
  33. <base-header title="预警联系人"></base-header>
  34. <div class="card-item">
  35. <div class="tools-panel" style="display: flex; align-items: center">
  36. <el-select v-model="personStatus" size="mini" popper-class="u-popper-select">
  37. <el-option label="在线" value="1"></el-option>
  38. <el-option label="离线" value="2"></el-option>
  39. </el-select>
  40. <div class="input-bg">
  41. <el-input v-model="personName" size="mini" placeholder="请输入名称" suffix-icon="el-icon-search"></el-input>
  42. </div>
  43. </div>
  44. <div class="person-list-container">
  45. <ul class="person-list">
  46. <li class="person-list-item" v-for="item in personList" :key="item.id">
  47. <div class="person-status" :class="{ on: item.status === '在线', off: item.status === '离线' }">{{ item.status }}</div>
  48. <div class="line"></div>
  49. <div class="person-info">
  50. <div class="person-name">
  51. <span>{{ item.name }}</span>
  52. <span class="phone">{{ item.phone }}</span>
  53. </div>
  54. <div class="person-remark">{{ item.remark }}</div>
  55. </div>
  56. <div style="position: absolute; top: 0.04rem; right: 0.06rem">
  57. <div class="btn details-btn" style="align-self: flex-start">详情</div>
  58. </div>
  59. </li>
  60. </ul>
  61. </div>
  62. </div>
  63. </div>
  64. </base-panel-right>
  65. <AddInspectionTask
  66. :visible="addTaskShow"
  67. :inspectionOptions="inspectionOptions"
  68. @closeAddTask="closeAddTask"
  69. @getSecurityPatrolList="getSecurityPatrolList"
  70. @refreshData="toLoadLeftList"
  71. />
  72. </div>
  73. </template>
  74. <script>
  75. import BasePanelRight from '@/components/base-panel/base-panel-right'
  76. import BaseHeader from '@/components/base-header/base-header'
  77. import { getSecurityPatrolList, endPatrol, deleteSecurityPatrol } from '@/api/securityPatrolApi'
  78. import AddInspectionTask from './addInspectionTask.vue'
  79. import moment from 'moment'
  80. import { confirm } from '@/utils'
  81. export default {
  82. name: 'sandMonitorRight',
  83. components: { BasePanelRight, BaseHeader, AddInspectionTask },
  84. props: {
  85. inspectionOptions: Array
  86. },
  87. data() {
  88. return {
  89. securityPatrolName: '',
  90. taskListData: [],
  91. personStatus: '1',
  92. addTaskShow: false,
  93. personName: '',
  94. personList: [
  95. {
  96. id: 1,
  97. status: '在线',
  98. name: '杨英',
  99. phone: '15319086888',
  100. remark: '四乱(乱占、乱堆、乱采、乱建)'
  101. },
  102. {
  103. id: 2,
  104. status: '离线',
  105. name: '马刚',
  106. phone: '15191001567',
  107. remark: '人员监控(下河、游泳、钓鱼、垃圾漂浮物等)'
  108. },
  109. {
  110. id: 3,
  111. status: '在线',
  112. name: '马刚',
  113. phone: '15191001567',
  114. remark: '生态区监控'
  115. },
  116. {
  117. id: 4,
  118. status: '在线',
  119. name: '谢智宇',
  120. phone: '15319940131',
  121. remark: '生态区监管'
  122. }
  123. ],
  124. currentTime: '',
  125. isPatrolling: false,
  126. currentPatrolIndex: 0,
  127. patrolInterval: null,
  128. inspectionPoint: [],
  129. taskInfo: {},
  130. xcId: null
  131. }
  132. },
  133. mounted() {
  134. this.getSecurityPatrolList()
  135. this.$globalEventBus.$on('closeVideoPlay', () => {
  136. this.stopPatrol()
  137. })
  138. this.$globalEventBus.$on('toPlayNextVideo', () => {
  139. this.goToNextPatrolPoint()
  140. })
  141. },
  142. methods: {
  143. refreshData() {
  144. this.getSecurityPatrolList()
  145. this.$emit('refreshLeftList')
  146. },
  147. toLoadLeftList() {
  148. this.$emit('refreshLeftList')
  149. },
  150. toXc(item) {
  151. this.currentTime = moment().format('YYYY-MM-DD HH:mm:ss')
  152. this.xcId = item.id
  153. this.inspectionPoint = item.inspectionPoints
  154. this.taskInfo = JSON.parse(JSON.stringify(item))
  155. this.isPatrolling = true
  156. this.currentPatrolIndex = 0
  157. this.goToNextPatrolPoint()
  158. // 设置定时器,每20秒切换到下一个点
  159. this.patrolInterval = setInterval(() => {
  160. this.goToNextPatrolPoint()
  161. }, 20000)
  162. },
  163. // 切换到下一个巡查点
  164. goToNextPatrolPoint() {
  165. if (this.currentPatrolIndex >= this.inspectionPoint.length) {
  166. this.stopPatrol()
  167. this.$globalEventBus.$emit('clickVideoInspectPlay', { visible: false, type: 'xc' })
  168. const endTime = moment().format('YYYY-MM-DD HH:mm:ss')
  169. endPatrol({ id: this.xcId, endTime: endTime, startTime: this.currentTime }).then(() => {
  170. this.refreshData()
  171. })
  172. return
  173. }
  174. this.$globalEventBus.$emit('clickVideoInspectPlay', { visible: false, type: 'xc' })
  175. setTimeout(() => {
  176. const point = this.inspectionPoint[this.currentPatrolIndex]
  177. this.flyToPoint(point)
  178. // 播放视频
  179. this.playVideo(point)
  180. this.currentPatrolIndex++
  181. }, 100)
  182. },
  183. flyToPoint(point) {
  184. window.map.flyToPoint([point.longitude, point.latitude], {
  185. radius: 5000, // 飞行距离目标点的距离
  186. duration: 2 // 飞行持续时间(秒)
  187. })
  188. },
  189. fetchUrl(item) {
  190. return new Promise((resolve, reject) => {
  191. window
  192. .requestSDK(
  193. '/ttvideo/video/player/getVideoRealtimeUrl',
  194. {
  195. deviceCode: item.deviceCode,
  196. channelCode: item.channelCode,
  197. netType: 2,
  198. protocolType: 9,
  199. streamType: 1
  200. },
  201. {},
  202. 'post'
  203. )
  204. .then(async (res) => {
  205. resolve(res)
  206. })
  207. })
  208. },
  209. playVideo(point) {
  210. this.fetchUrl(point).then((res) => {
  211. if (res.code == 200) {
  212. point.url = res.data.streamUrl
  213. this.$globalEventBus.$emit('clickVideoInspectPlay', { taskInfo: this.taskInfo, point: point, visible: true, type: 'xc' })
  214. } else if (res.code == 4001) {
  215. this.$message.warning(JSON.parse(res.msg).resultMsg)
  216. }
  217. })
  218. },
  219. stopPatrol() {
  220. this.isPatrolling = false
  221. clearInterval(this.patrolInterval)
  222. },
  223. addTask() {
  224. this.addTaskShow = true
  225. },
  226. closeAddTask() {
  227. this.addTaskShow = false
  228. this.getSecurityPatrolList()
  229. this.$emit('refreshLeftList')
  230. },
  231. getSecurityPatrolList() {
  232. getSecurityPatrolList({ securityPatrolName: this.securityPatrolName, finishedState: 0 }).then((res) => {
  233. this.taskListData = res.data.records
  234. })
  235. },
  236. searchByName(name) {
  237. this.securityPatrolName = name
  238. this.getSecurityPatrolList()
  239. },
  240. handleDelete(row) {
  241. confirm()
  242. .then(() => {
  243. deleteSecurityPatrol(row.id).then((res) => {
  244. if (res.code === 200) {
  245. this.$message({ message: '删除成功', type: 'success' })
  246. this.getSecurityPatrolList()
  247. } else {
  248. this.$message({ message: 'res.msg , type: 'error' })
  249. }
  250. })
  251. })
  252. .catch(() => {})
  253. }
  254. },
  255. destroyed() {
  256. this.$globalEventBus.$off('closeVideoPlay')
  257. }
  258. }
  259. </script>
  260. <style scoped lang="scss">
  261. @import url('@/assets/scss/px-to-rem.scss');
  262. .safety-inspection-right-container {
  263. position: relative;
  264. height: 100%;
  265. z-index: 1;
  266. .card-item {
  267. position: relative;
  268. width: 3.68rem;
  269. padding: px-to-rem(10);
  270. margin-bottom: px-to-rem(19);
  271. background: url('@/assets/image/common/areaBg.png') no-repeat 0 0 / 100% 100%;
  272. :deep(.el-input__inner) {
  273. background: transparent;
  274. color: #fff;
  275. border: none;
  276. }
  277. }
  278. .tools-panel {
  279. .input-bg {
  280. margin: px-to-rem(10) 0;
  281. background: url('@/assets/image/safety-inspection/search-bg.png') no-repeat 0 0 / 100% 100%;
  282. }
  283. :deep(.el-select) {
  284. margin-right: px-to-rem(10);
  285. .el-input__inner {
  286. background: transparent;
  287. border: 1px solid;
  288. border-color: #4f9fff;
  289. width: px-to-rem(100);
  290. color: #fff;
  291. }
  292. }
  293. }
  294. .task-list-container {
  295. .task-table-list {
  296. &-title {
  297. display: flex;
  298. justify-content: space-between;
  299. color: #f2f6ff;
  300. font-size: px-to-rem(16);
  301. padding: 0 px-to-rem(10);
  302. height: px-to-rem(35);
  303. line-height: px-to-rem(35);
  304. background: #244e81;
  305. }
  306. &-item {
  307. display: flex;
  308. justify-content: space-between;
  309. color: #fff;
  310. font-size: px-to-rem(16);
  311. padding: px-to-rem(4) px-to-rem(10);
  312. margin-bottom: px-to-rem(1.8);
  313. height: px-to-rem(35);
  314. line-height: px-to-rem(35);
  315. background: #153057;
  316. box-sizing: border-box;
  317. span {
  318. display: inline-block;
  319. width: 40%;
  320. }
  321. span:first-child {
  322. white-space: nowrap;
  323. overflow: hidden;
  324. text-overflow: ellipsis;
  325. }
  326. span:last-child {
  327. width: px-to-rem(50);
  328. text-align: right;
  329. }
  330. }
  331. .item-area {
  332. height: px-to-rem(324);
  333. overflow: auto;
  334. &::-webkit-scrollbar {
  335. display: none;
  336. }
  337. }
  338. }
  339. }
  340. .person-list-container {
  341. .person-list {
  342. &-item {
  343. position: relative;
  344. display: flex;
  345. justify-content: space-between;
  346. height: px-to-rem(64);
  347. border: 1px solid #0670b4;
  348. border-radius: px-to-rem(50) 0 0 px-to-rem(50);
  349. padding: px-to-rem(10);
  350. margin-bottom: px-to-rem(20);
  351. .person-status {
  352. width: px-to-rem(41);
  353. height: px-to-rem(41);
  354. line-height: px-to-rem(41);
  355. text-align: center;
  356. padding: px-to-rem(2);
  357. border-radius: 50%;
  358. font-weight: bold;
  359. font-size: px-to-rem(14);
  360. }
  361. .on {
  362. background: rgba(13, 201, 133, 0.1);
  363. border: 1px solid #0dc985;
  364. color: #0dc985;
  365. }
  366. .off {
  367. background: rgba(145, 158, 183, 0.1);
  368. border: 1px solid #919eb7;
  369. color: #919eb7;
  370. }
  371. .line {
  372. width: px-to-rem(1);
  373. height: px-to-rem(42);
  374. background: #919eb7;
  375. margin: 0 px-to-rem(10);
  376. }
  377. .person-info {
  378. display: flex;
  379. flex-direction: column;
  380. flex: 1;
  381. .person-name {
  382. font-weight: bold;
  383. font-size: px-to-rem(16);
  384. color: #fff;
  385. font-weight: 500;
  386. margin-bottom: px-to-rem(5);
  387. .phone {
  388. margin-left: px-to-rem(15);
  389. font-weight: bold;
  390. font-size: px-to-rem(14);
  391. color: #919eb7;
  392. }
  393. }
  394. .person-remark {
  395. font-size: px-to-rem(12);
  396. color: #919eb7;
  397. margin-bottom: px-to-rem(10);
  398. }
  399. }
  400. }
  401. }
  402. }
  403. .details-btn {
  404. width: px-to-rem(36);
  405. height: px-to-rem(22);
  406. }
  407. .add-btn {
  408. width: px-to-rem(90);
  409. height: px-to-rem(24);
  410. }
  411. .btn {
  412. cursor: pointer;
  413. background: linear-gradient(0deg, #468adb 0%, #366dae 100%);
  414. border-radius: px-to-rem(4);
  415. border: px-to-rem(1) solid rgba(255, 255, 255, 0.8);
  416. font-size: px-to-rem(14);
  417. color: #ffffff;
  418. text-align: center;
  419. }
  420. }
  421. .vSelect {
  422. :deep(.el-input__inner) {
  423. background: rgba(79, 159, 255, 0.3);
  424. border-color: #4f9fff;
  425. width: px-to-rem(96);
  426. border-radius: px-to-rem(4);
  427. color: #fff;
  428. padding: 0 px-to-rem(2);
  429. }
  430. :deep(.el-input__suffix .el-input__icon) {
  431. width: px-to-rem(18) !important;
  432. }
  433. }
  434. .vSelect:last-child {
  435. margin-left: px-to-rem(10);
  436. :deep(.el-input__inner) {
  437. width: px-to-rem(58);
  438. }
  439. }
  440. </style>