box.html 78 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <title>视频插件——弹框模式</title>
  7. <link data-n-head="true" rel="icon" type="image/x-icon" href="/img/favicon.ico">
  8. <link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
  9. <link rel="stylesheet" href="./css/demo.css">
  10. <link rel="stylesheet" href="./lib/ztree/css/zTreeStyle.css">
  11. <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  12. <script src="https://unpkg.com/element-ui/lib/index.js"></script>
  13. <script src="https://unpkg.com/http-vue-loader"></script>
  14. <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
  15. <script src="./lib/DHWs.js"></script>
  16. <script src="./lib/jquery.js"></script>
  17. <script type='text/javascript' src='./lib/crypto-js.min.js'></script>
  18. <style type="text/css">
  19. [v-cloak] {
  20. display: none;
  21. }
  22. .abow_dialog {
  23. display: flex;
  24. justify-content: center;
  25. align-items: Center;
  26. overflow: hidden;
  27. }
  28. .abow_dialog .el-dialog {
  29. margin: 0 auto !important;
  30. height: 90%;
  31. overflow: hidden;
  32. width: 90%;
  33. }
  34. .abow_dialog .el-dialog .el-dialog__body {
  35. position: absolute;
  36. left: 0;
  37. top: 54px;
  38. bottom: 0;
  39. right: 0;
  40. padding: 0;
  41. z-index: 1;
  42. overflow: hidden;
  43. overflow-y: auto;
  44. }
  45. .el-footer {
  46. font-size: 13px;
  47. }
  48. .timeline-main {
  49. word-break: normal;
  50. }
  51. </style>
  52. </head>
  53. <body>
  54. <div id="app">
  55. <el-button @click="open">打开视频插件</el-button>
  56. <!-- 录像下载 -->
  57. <el-dialog title="录像下载" class="console-info-pop" ref="downLoadDialog" :visible.sync="downLoadDialogVisible" :close-on-click-modal="false" width="850px">
  58. <el-form label-width="100px" class="record-date-form">
  59. <el-form-item class="ellipsis" label="录像日期:">
  60. <el-date-picker
  61. v-model="recordDate"
  62. type="date"
  63. value-format="yyyy-MM-dd"
  64. size="mini"
  65. @change="recordDateChange"
  66. placeholder="选择日期">
  67. </el-date-picker>
  68. </el-form-item>
  69. </el-form>
  70. <el-table
  71. highlight-current-row
  72. size="mini"
  73. border
  74. height="380"
  75. :data="recordData[hwnd]"
  76. v-loading="loading">
  77. <el-table-column prop="strBeginTime" label="开始时间" width="150" show-overflow-tooltip align="center"></el-table-column>
  78. <el-table-column prop="strEndTime" label="结束时间" width="150" show-overflow-tooltip align="center"></el-table-column>
  79. <el-table-column prop="strName" label="文件名" show-overflow-tooltip align="center">
  80. <template slot-scope="scope">
  81. <span>{{windowMap.get(hwnd + 1) ? windowMap.get(hwnd + 1).orgName +'_'+ windowMap.get(hwnd + 1).channelName +'_'+ scope.row.strBeginTime.replace(/\s|-|:/g,"") +'_'+ scope.row.strEndTime.replace(/\s|-|:/g,"") +'.mp4': ''}}</span>
  82. </template>
  83. </el-table-column>
  84. <el-table-column prop="channelName" label="通道名称" show-overflow-tooltip align="center">
  85. <template slot-scope="scope">
  86. <span>{{windowMap.get(hwnd + 1) ? windowMap.get(hwnd + 1).channelName : ''}}</span>
  87. </template>
  88. </el-table-column>
  89. <el-table-column prop="nFileLength" label="文件大小" width="70" show-overflow-tooltip align="center">
  90. <template slot-scope="scope">
  91. <span>{{Math.round(scope.row.nFileLength / 1024 / 1024, 1)}}MB</span>
  92. </template>
  93. </el-table-column>
  94. <el-table-column label="操作" width="90" align="center">
  95. <template slot-scope="scope">
  96. <el-button type="text" size="small" @click="downLoadByFile(scope.row, scope.$index)">下载</el-button>
  97. </template>
  98. </el-table-column>
  99. </el-table>
  100. </el-dialog>
  101. <!-- 按时间下载 -->
  102. <el-dialog title="按时间下载" class="console-info-pop" ref="timeDownLoadDialog" :visible.sync="timeDownLoadDialogVisible" :close-on-click-modal="false" width="540px">
  103. <el-form ref="downloadForm" label-width="100px" :model="downloadForm" class="w-precent-100">
  104. <el-form-item class="ellipsis" label="通道名称:">
  105. <span>{{windowMap.get(hwnd + 1) ? windowMap.get(hwnd + 1).channelName : ''}}</span>
  106. </el-form-item>
  107. <el-form-item class="ellipsis" label="时间区间:" prop="timeRangeValue" :rules="{required: true, message: '时间区间不能为空', trigger: 'blur'}">
  108. <el-date-picker
  109. v-model="downloadForm.timeRangeValue"
  110. type="datetimerange"
  111. range-separator="至"
  112. start-placeholder="开始日期"
  113. end-placeholder="结束日期"
  114. @blur="dateRangeBlur"
  115. @focus="dateRangeFocus"
  116. value-format="yyyy-MM-dd HH:mm:ss"
  117. :default-time="['00:00:00', '23:59:59']">
  118. </el-date-picker>
  119. </el-form-item>
  120. </el-form>
  121. <span slot="footer" class="dialog-footer">
  122. <el-button @click="timeDownLoadDialogVisible = false">取 消</el-button>
  123. <el-button type="primary" @click="downLoadByTime">确 定</el-button>
  124. </span>
  125. </el-dialog>
  126. <el-dialog title="视频播放插件" class="console-info-pop abow_dialog" ref="pluginDialog" :visible.sync="pluginDialogVisible" :close-on-click-modal="false" >
  127. <div v-cloak style="height:100%;">
  128. <el-container class="shop-video" v-cloak>
  129. <el-aside :width="asideWidth" class="mgb30">
  130. <ul class="left-ul" :style="{width: asideWidth}">
  131. <li class="left-li float-l" @click="showTab(1)" class="active">
  132. <span>通道</span>
  133. <span class="current-icon"></span>
  134. </li>
  135. </ul>
  136. <div class="shop-tree-box video-shop-tree" :class="holderFold ? 'holder-fold' : 'holder-unfold'">
  137. <div class="input-box-client">
  138. <search-input :max-len="25" mode="search" placeholdertip="请输入关键字搜索"
  139. @search="handleTreeEnter"></search-input>
  140. </div>
  141. <ul id="videoShopTree" class="ztree lighter-scroll-bar" ref="videoShopTree"></ul>
  142. <el-alert v-if="noNode" :closable="false" title="没有找到节点" type="warning" show-icon></el-alert>
  143. </div>
  144. <holder @holder-click="holderClick" @holder-handle="holderHandle" @switch-fold="switchFold" :brand="brand"></holder>
  145. </el-aside>
  146. <el-container>
  147. <el-main id="cBox" ref="cBox" class="cs-box relative">
  148. </el-main>
  149. <el-row class="w-precent-100 h40">
  150. <el-col :span="12" class="h40" v-if="windowMap.get(hwnd + 1) && [1,2].includes(windowMap.get(hwnd + 1).playStatus)">
  151. <span :class="recording ? 'recording-normal' : 'record-normal'"
  152. :title="recording ? '停止录像' : '本地录像'" @click="localRecord"></span>
  153. <span :class="audioing ? 'audio-normal' : 'audio-no-normal'"
  154. :title="audioing ? '关闭声音' : '打开声音'" @click="audioOperation"></span>
  155. <span class="snap1-icon" title="截图" @click="snapPic"></span>
  156. <span v-show="windowMap.get(hwnd + 1).playStatus === 1" :class="talking ? 'talking-normal' : 'talk-normal'" :title="talking ? '关闭对讲' : '开启对讲'"
  157. @click="talkOperation"></span>
  158. <span v-show="windowMap.get(hwnd + 1).playStatus === 1" class="tool-span" :title="definitionMode === 1 ? '切换高清' : '切换标清'" @click="switchClarity">
  159. {{definitionMode === 1 ? '标清' : '高清'}}</span>
  160. <span class="stop-normal" title="停止" @click="stopPlay(hwnd)"></span>
  161. <p class="inline-block mg-0" v-show="windowMap.get(hwnd + 1) && windowMap.get(hwnd + 1).playStatus === 2">
  162. <span class="slow-normal" title="后退15秒" @click="recordBackward"></span>
  163. <span :class="playing ? 'play-normal' : 'pause-normal'" :title="playing ? '暂停' : '播放'" @click="recordPause"></span>
  164. <span class="speed-normal" title="前进15秒" @click="recordForward"></span>
  165. <el-dropdown placement="bottom-end" @command="multipleRecord">
  166. <span class="el-dropdown-link" title="播放倍数">
  167. {{multiple}}X<i class="el-icon-arrow-down el-icon--right"></i>
  168. </span>
  169. <el-dropdown-menu slot="dropdown">
  170. <el-dropdown-item command="1">1X</el-dropdown-item>
  171. <el-dropdown-item command="2">2X</el-dropdown-item>
  172. <el-dropdown-item command="4">4X</el-dropdown-item>
  173. <el-dropdown-item command="6">6X</el-dropdown-item>
  174. <el-dropdown-item command="8">8X</el-dropdown-item>
  175. </el-dropdown-menu>
  176. </el-dropdown>
  177. </p>
  178. </el-col>
  179. <el-col :span="12" class="h40" v-else></el-col>
  180. <el-col :span="12" class="text-right h40">
  181. <span class="p4-normal-icon" @click="stepLength = 0" title="1分屏"></span>
  182. <el-slider class="el-slider-step" v-model="stepLength" :show-tooltip="false" :min="0" :max="5" @input="stepLengthInput"></el-slider>
  183. <span class="p16-normal-icon" @click="stepLength = 5" title="16分屏"></span>
  184. <span class="tool-span w34" :title="recordType === 1 ? '切换设备录像' : '切换云录像'" @click="switchRecordType">
  185. {{recordType === 1 ? '云' : '设备'}}</span>
  186. <span id="record-download-span" class="download-normal-icon" @click="showDownloadMode = true" title="录像下载"></span>
  187. <ul v-show="showDownloadMode" class="download-ul" v-clickoutside="toggleMode">
  188. <li v-if="recordType !== 1" @click="openByTimeDownloadDialog">按时间下载</li>
  189. <li @click="openDownloadDialog">按文件下载</li>
  190. </ul>
  191. <span class="closeall-normal-icon" title="关闭所有" @click="closeAllVideo"></span>
  192. </el-col>
  193. </el-row>
  194. <div class="timeline-div">
  195. <i class="el-icon-arrow-left" @click="prev"></i>
  196. <span class="inline-block w30">{{dateArr[1]}}月</span>
  197. <div class="inline-block date-panel">
  198. <p v-for="item in dayNum" :key="item" class="w-precent-3 inline-block">
  199. <span class="time-point" :class="item === dateArr[2] ? 'time-point-active' : ''" @click="timePointClick(item)">{{item | dealZero}}</span>
  200. </p>
  201. </div>
  202. <i class="el-icon-arrow-right" @click="next"></i>
  203. <div class="inline-block select-date w140">
  204. <el-date-picker v-model="selectDate" class="select-date" type="datetime" placeholder="选择日期"
  205. @change="updateDateArr" value-format="yyyy-MM-dd HH:mm:ss" @blur="dateBlur" @focus="dateFocus" default-time="12:00:00"></el-date-picker>
  206. </div>
  207. </div>
  208. <el-footer height="104px" class="relative">
  209. <div class="timeline-no">
  210. <p v-for="item in screenNum[screenNum.length - 1]" class="timeline-no-p" :key="'p'+item" :class="hwnd === item - 1 ? 'font-red' : ''">
  211. <span class="timeline-no-span">{{item}}</span>
  212. <span class="timeline-title" :title="windowMap.get(item) ? windowMap.get(item).channelName : ''">
  213. {{windowMap.get(item) ? windowMap.get(item).channelName : ''}}
  214. </span>
  215. </p>
  216. </div>
  217. <timer v-for="item in screenNum[screenNum.length - 1]" :ref="'timerRef'+item" :key="'timer'+item" @timer-change="timerChange($event, item, true)" :record-infos="recordInfoObj[item]"></timer>
  218. </el-footer>
  219. </el-container>
  220. </el-container>
  221. </div>
  222. </el-dialog>
  223. </div>
  224. </body>
  225. <script type="module">
  226. import { getShopTree, changeStatus, onDataSuccess, GetLeToken } from './lib/demo.js';
  227. import fetch from './lib/ajax.js'
  228. import utils from './lib/utils.js'
  229. import config from './config.js'
  230. const DHWsInstance = DHWs.getInstance({});
  231. new Vue({
  232. el: '#app',
  233. data: function () {
  234. return {
  235. // 窗口map,key为窗口号,从1开始,value为窗口信息,包括窗口的设备信息、通道信息、播放状态等等
  236. windowMap: new Map(),
  237. resizeFn: null,
  238. // 支持的屏幕数组下标值
  239. stepLength: 1,
  240. // 支持的屏幕数组
  241. screenNum: [1, 4, 6, 8, 9, 16],
  242. ws: DHWsInstance,
  243. appName: "appbox",
  244. appId: "",
  245. isLogin: false,
  246. // 控件的唯一编码
  247. ctrlCode: "ctrlbox",
  248. // 当前选择的渠道ID
  249. channelId: "",
  250. // 清晰度模式
  251. definitionMode: 1,
  252. // 设备ID
  253. deviceId: '',
  254. //控件信息
  255. ctrlPosX: 205,
  256. ctrlPosY: 0,
  257. ctrlWidth: 1490,
  258. ctrlHeight: 760,
  259. // 通道map,key为channelId,value为通道信息
  260. channelMap: new Map(),
  261. // 当前激活的窗口号,从0开始
  262. hwnd: 0,
  263. recording: false,
  264. audioing: false,
  265. talking: false,
  266. playing: true,
  267. asideWidth: '205px',
  268. // 选择的日期时间
  269. selectDate: '',
  270. // 选择的日期数组,格式[年, 月, 日]
  271. dateArr: [],
  272. isFocus: false,
  273. isRangeFocus: false,
  274. // 录像查看的参数,因为录像查看返回的信息是异步返回,所以需要保存,以便用于录像回放
  275. recordParam: {},
  276. // 录像片段信息,用于传值给时间轴组件,设置录像片段背景
  277. recordInfoObj: {},
  278. timerChangeFn: null,
  279. // 录像下载弹框里的录像数据
  280. recordData: {},
  281. downLoadDialogVisible: false,
  282. timeDownLoadDialogVisible: false,
  283. pluginDialogVisible: false,
  284. //云台操作折叠
  285. holderFold: true,
  286. loading: false,
  287. showDownloadMode: false,
  288. // 录像查看后是否需要回放
  289. needPlayBack: true,
  290. // 按时间下载表单
  291. downloadForm: {
  292. timeRangeValue: ''
  293. },
  294. // 要裁剪的位置信息数组
  295. cutList: [],
  296. byTimeDownLoad: false,
  297. // 录像类型 0:设备录像,1:云录像
  298. recordType: 0,
  299. // 播放倍数,默认1倍数
  300. multiple: 1,
  301. // 录像日期
  302. recordDate: '',
  303. // 设备品牌信息
  304. brand: 'general',
  305. // 播放中的结点,用于模糊查询后更新图标
  306. playingNodes: [],
  307. searchKey: '',
  308. //是否无节点
  309. noNode: false,
  310. //乐橙token
  311. lcToken: '',
  312. //历史门店浏览
  313. hisStoreList: [],
  314. treeObj: '',
  315. //选择菜单index
  316. activeIndex: '1',
  317. // 拖拽显示的标志位
  318. showNarrow: false
  319. }
  320. },
  321. components: {
  322. 'holder': httpVueLoader('./component/holder.vue'),
  323. 'timer': httpVueLoader('./component/timer.vue'),
  324. 'search-input': httpVueLoader('./component/search-input.vue'),
  325. },
  326. computed: {
  327. // 当前选择月份的天数
  328. dayNum() {
  329. if (this.dateArr.length > 0) {
  330. return new Date(this.dateArr[0], this.dateArr[1], 0).getDate()
  331. } else {
  332. return 0
  333. }
  334. }
  335. },
  336. watch: {
  337. // 按文件下载弹框打开后,要裁剪视频窗口
  338. downLoadDialogVisible(val) {
  339. if (val) {
  340. this.$nextTick(() => {
  341. setTimeout(() => {
  342. this.cutDownloadDialog()
  343. }, 0)
  344. });
  345. } else {
  346. this.cut([])
  347. }
  348. },
  349. // 按时间下载弹框打开后,要裁剪视频窗口
  350. timeDownLoadDialogVisible(val) {
  351. if (val) {
  352. this.$nextTick(() => {
  353. setTimeout(() => {
  354. this.cutTimeDownloadDialog()
  355. }, 0)
  356. });
  357. } else {
  358. this.cut([])
  359. }
  360. },
  361. // 插件弹框关闭后,要销毁视频窗口
  362. pluginDialogVisible(val) {
  363. if (!val) {
  364. this.unregister()
  365. this.isLogin = false
  366. this.windowMap.clear()
  367. for (const key in this.recordInfoObj) {
  368. this.$set(this.recordInfoObj, key, [])
  369. }
  370. }
  371. },
  372. },
  373. filters: {
  374. // 日期补0
  375. dealZero(val) {
  376. return val <= 9 ? '0' + val : val
  377. }
  378. },
  379. directives: {
  380. clickoutside: {
  381. bind (el, binding, vnode) {
  382. function documentHandler (e) {
  383. if (el.contains(e.target)) {
  384. return false;
  385. }
  386. if (binding.expression) {
  387. binding.value(e);
  388. }
  389. }
  390. el.__vueClickOutside__ = documentHandler;
  391. document.addEventListener('click', documentHandler);
  392. },
  393. unbind (el, binding) {
  394. document.removeEventListener('click', el.__vueClickOutside__);
  395. delete el.__vueClickOutside__;
  396. }
  397. }
  398. },
  399. methods: {
  400. // 连接
  401. connect() {
  402. this.ws.connectSocket({
  403. port: config.websocket_port,
  404. });
  405. window.addEventListener('message', (e) => { // 监听 message 事件
  406. if (e.data.method === 'connectResult' && !e.data.success) {
  407. // 连接客户端失败
  408. this.$notify({
  409. title: '连接提示',
  410. message: '连接客户端失败',
  411. type: 'warning',
  412. position: 'bottom-right'
  413. });
  414. this.openDHPlayer();
  415. }
  416. });
  417. },
  418. // 注销接口
  419. unregister() {
  420. this.ws.unregister({
  421. appName: this.appName,
  422. });
  423. },
  424. // 注册接口
  425. register() {
  426. this.ws.detectConnectQt().then((res) => {
  427. if (res) {
  428. // 连接客户端成功
  429. this.ws.register({
  430. appName: this.appName,
  431. });
  432. } else {
  433. // // 连接客户端失败
  434. // this.$notify({
  435. // title: '连接提示',
  436. // message: '连接客户端失败',
  437. // type: 'warning',
  438. // position: 'bottom-right'
  439. // });
  440. // this.openDHPlayer();
  441. }
  442. });
  443. },
  444. // 未启动客户端的时候,主动打开客户端
  445. openDHPlayer() {
  446. var t = document.createElement('iframe');
  447. t.style.display = 'none';
  448. t.src = 'ECloudCtrl://';
  449. document.body.appendChild(t);
  450. setTimeout(function() {
  451. document.body.removeChild(t)
  452. }, 1000 * 3)
  453. },
  454. // 创建控件接口
  455. create() {
  456. if (!this.isLogin) {
  457. this.$notify({
  458. title: '提示',
  459. message: '请先注册应用名称',
  460. type: 'warning',
  461. position: 'bottom-right'
  462. });
  463. return false;
  464. }
  465. const params = [
  466. {
  467. ctrlType: 'playerWin',
  468. ctrlCode: this.ctrlCode,
  469. ctrlProperty: { "wndCount": 4 },
  470. visible: true,
  471. domId: "cBox",
  472. posX: this.ctrlPosX,
  473. posY: this.ctrlPosY,
  474. width: this.$refs.cBox.$el.clientWidth,
  475. height: this.$refs.cBox.$el.clientHeight,
  476. },
  477. ];
  478. this.ws.createCtrl(params).then((res) => {
  479. }).catch((e) => {
  480. console.log(e);
  481. });
  482. this.ws.on("createCtrlResult", (res) => {
  483. if (res && res.appId === this.appId && res.appName === this.appName) {
  484. this.queryVersion()
  485. this.setPos()
  486. }
  487. });
  488. },
  489. // 定位控件
  490. setPos() {
  491. if (!this.isLogin) {
  492. return false;
  493. }
  494. const params = [
  495. {
  496. ctrlCode: this.ctrlCode,
  497. posX: this.ctrlPosX,
  498. posY: this.ctrlPosY,
  499. width: this.$refs.cBox.$el.clientWidth,
  500. height: this.$refs.cBox.$el.clientHeight,
  501. }
  502. ];
  503. this.ws.setCtrlPos(params);
  504. },
  505. // 裁剪
  506. cut(cutList) { // 调用设置控件属性接口 修改剪切属性
  507. if (!this.isLogin) {
  508. this.$notify({
  509. title: '提示',
  510. message: '请先注册应用名称',
  511. type: 'warning',
  512. position: 'bottom-right'
  513. });
  514. return false;
  515. }
  516. const params = [
  517. {
  518. ctrlCode: this.ctrlCode,
  519. cutList: cutList
  520. }
  521. ];
  522. this.ws.setCtrlPos(params);
  523. },
  524. // 云台点击,8个方向
  525. holderClick(data) {
  526. console.log("云台点击:" + JSON.stringify(data))
  527. if (this.windowMap.get(this.hwnd + 1) == null) {
  528. console.log("当前窗口没有有效视频播放,无法操作云台")
  529. return
  530. }
  531. let channelInfo = this.channelMap.get(this.channelId)
  532. let param = {
  533. "appId": this.appId,
  534. "appName": this.appName,
  535. "ctrlCode": this.ctrlCode,
  536. "params": {
  537. direct: data.direct,
  538. bStop: data.bStop,
  539. step: data.stepLength,
  540. token: this.lcToken,
  541. user: channelInfo.user,
  542. passwd: channelInfo.passwd,
  543. deviceId: channelInfo.deviceId,
  544. channelId: channelInfo.channelId
  545. }
  546. }
  547. this.ws.transparent('ptzControlDir', param)
  548. },
  549. // 云台操作,聚焦 光圈 变倍
  550. holderHandle(data) {
  551. console.log("云台操作:" + JSON.stringify(data))
  552. if (this.windowMap.get(this.hwnd + 1) == null) {
  553. console.log("当前窗口没有有效视频播放,无法操作云台")
  554. return
  555. }
  556. const method = data.method
  557. let channelInfo = this.channelMap.get(this.channelId)
  558. let param = {
  559. "appId": this.appId,
  560. "appName": this.appName,
  561. "ctrlCode": this.ctrlCode,
  562. "params": {
  563. bStop: data.bStop,
  564. token: this.lcToken,
  565. user: channelInfo.user,
  566. passwd: channelInfo.passwd,
  567. deviceId: channelInfo.deviceId,
  568. channelId: channelInfo.channelId
  569. }
  570. }
  571. if (method === 'ptzApertureOperation') {
  572. param.params.apertureAdd = data.add
  573. } else if (method === 'ptzFocusOperation') {
  574. param.params.focusAdd = data.add
  575. } else if (method === 'ptzZoomOperation') {
  576. param.params.zoomAdd = data.add
  577. }
  578. this.ws.transparent(method, param)
  579. },
  580. // 云台操作面板折叠打开的切换
  581. switchFold(val) {
  582. this.holderFold = val
  583. },
  584. /**
  585. * @method openVideo() 播放通道视频
  586. */
  587. openVideo(channelItems, storeName) {
  588. let that = this;
  589. console.log('start to openVideo channels==========' + JSON.stringify(channelItems))
  590. channelItems = Array.isArray(channelItems) ? channelItems : [channelItems]
  591. GetLeToken().then((res) => {
  592. let [token, len, params] = [res.data.data.token, channelItems.length, {}]
  593. that.lcToken = token;
  594. params.channels = []
  595. params.module = 0
  596. let channelList = []
  597. for (let i = 0; i < len; i++) {
  598. let channelItem = channelItems[i]
  599. if (channelItem.onlineStatus != 0) {
  600. channelList.push({
  601. channelId: channelItem.channelId,
  602. definitionMode: 1,
  603. deviceId: channelItem.deviceId,
  604. hwnd: this.hwnd,
  605. token: that.lcToken,
  606. devAbility: channelItem.ability,
  607. devModel: channelItem.deviceModel,
  608. orgCode: channelItem.storeId,
  609. orgName: channelItem.storeName,
  610. accessSource: channelItem.accessSource,
  611. channelName: channelItem.channelName,
  612. brand: channelItem.brand
  613. })
  614. this.channelMap.set(channelItem.channelId, {
  615. user: utils.rDecodeDes('dh-qQ3j!retailcloud', channelItem.devUsername) || '',
  616. passwd: utils.rDecodeDes('dh-qQ3j!retailcloud', channelItem.devPassword) || '',
  617. deviceId: channelItem.deviceId,
  618. channelId: channelItem.channelId
  619. })
  620. }
  621. this.windowMap.set(this.hwnd + 1, {
  622. channelId: channelItem.channelId,
  623. deviceId: channelItem.deviceId,
  624. channelName: channelItem.channelName,
  625. devAbility: channelItem.ability,
  626. devModel: channelItem.deviceModel,
  627. orgCode: channelItem.storeId,
  628. orgName: channelItem.storeName,
  629. accessSource: channelItem.accessSource,
  630. brand: channelItem.brand,
  631. recordType: 0, //默认初始化为设备录像
  632. multiple: 1, //默认初始化1倍数播放
  633. playing: true, //默认初始化是播放状态
  634. recording: false, //默认初始化是非录像状态
  635. talking: false, //默认初始化是非对讲状态
  636. initPlay: true //初始化播放
  637. })
  638. // 传值录像片段给timer组件,设置录像片段背景
  639. this.$set(this.recordInfoObj, this.hwnd + 1, [])
  640. }
  641. this.$forceUpdate();
  642. if (channelList.length > 0) {
  643. let now = new Date();
  644. this.dateArr = [now.getFullYear(), now.getMonth() + 1, now.getDate()]
  645. this.updateSelectDate()
  646. this.playReal(channelList)
  647. }
  648. })
  649. },
  650. /**
  651. * @method playReal() 实时预览
  652. */
  653. playReal(channelList) {
  654. let param = {
  655. "appId": this.appId,
  656. "appName": this.appName,
  657. "method": "playReal",
  658. "ctrlCode": this.ctrlCode,
  659. "params": {
  660. "array": channelList
  661. }
  662. }
  663. this.ws.transparent('playReal', param)
  664. },
  665. /**
  666. * @method stepLengthInput() 视频窗口数目的变化
  667. */
  668. stepLengthInput(num) {
  669. let param = {
  670. "appId": this.appId,
  671. "appName": this.appName,
  672. "ctrlCode": this.ctrlCode,
  673. "params": {
  674. "wndCount": this.screenNum[num]
  675. }
  676. }
  677. if (this.appId) {
  678. this.ws.transparent('setWndCount', param)
  679. }
  680. },
  681. /**
  682. * @method localRecord() 录像
  683. */
  684. localRecord() {
  685. this.recording = !this.recording
  686. let param = {
  687. "appId": this.appId,
  688. "appName": this.appName,
  689. "ctrlCode": this.ctrlCode,
  690. "params": {
  691. "bStart": this.recording,
  692. "hwnd": this.hwnd
  693. }
  694. }
  695. this.ws.transparent('localRecord', param)
  696. // 记录窗口信息
  697. let windowInfo = this.windowMap.get(this.hwnd + 1)
  698. if (windowInfo != null) {
  699. windowInfo.recording = this.recording
  700. }
  701. },
  702. /**
  703. * @method audioOperation() 声音操作
  704. */
  705. audioOperation() {
  706. this.audioing = !this.audioing
  707. let param = {
  708. "appId": this.appId,
  709. "appName": this.appName,
  710. "ctrlCode": this.ctrlCode,
  711. "params": {
  712. "bStart": this.audioing,
  713. "hwnd": this.hwnd
  714. }
  715. }
  716. this.ws.transparent('audioOperation', param)
  717. },
  718. /**
  719. * @method talkOperation() 对讲操作
  720. */
  721. talkOperation() {
  722. this.talking = !this.talking
  723. let param = {
  724. "appId": this.appId,
  725. "appName": this.appName,
  726. "ctrlCode": this.ctrlCode,
  727. "params": {
  728. "bStart": this.talking,
  729. "hwnd": this.hwnd
  730. }
  731. }
  732. this.ws.transparent('talkOperation', param)
  733. // 记录窗口信息
  734. let windowInfo = this.windowMap.get(this.hwnd + 1)
  735. if (windowInfo != null) {
  736. windowInfo.talking = this.talking
  737. }
  738. },
  739. /**
  740. * @method switchClarity() 切换清晰度
  741. */
  742. switchClarity() {
  743. this.definitionMode = this.definitionMode === 0 ? 1 : 0
  744. let channel = { channelId: this.channelId, definitionMode: this.definitionMode, deviceId: this.deviceId, token: this.lcToken, hwnd: this.hwnd }
  745. this.playReal([channel])
  746. },
  747. /**
  748. * @method snapPic() 截图
  749. */
  750. snapPic() {
  751. let param = {
  752. "appId": this.appId,
  753. "appName": this.appName,
  754. "ctrlCode": this.ctrlCode,
  755. "params": {
  756. "hwnd": this.hwnd
  757. }
  758. }
  759. this.ws.transparent('snapPic', param)
  760. },
  761. /**
  762. * @method recordBackward() 快退15秒
  763. */
  764. recordBackward() {
  765. let param = {
  766. "appId": this.appId,
  767. "appName": this.appName,
  768. "ctrlCode": this.ctrlCode,
  769. "params": {
  770. "hwnd": this.hwnd
  771. }
  772. }
  773. this.ws.transparent('recordBackward', param)
  774. },
  775. /**
  776. * @method recordForward() 快进15秒
  777. */
  778. recordForward() {
  779. let param = {
  780. "appId": this.appId,
  781. "appName": this.appName,
  782. "ctrlCode": this.ctrlCode,
  783. "params": {
  784. "hwnd": this.hwnd
  785. }
  786. }
  787. this.ws.transparent('recordForward', param)
  788. },
  789. /**
  790. * @method recordPause() 录像暂停/继续播放
  791. */
  792. recordPause() {
  793. let param = {
  794. "appId": this.appId,
  795. "appName": this.appName,
  796. "ctrlCode": this.ctrlCode,
  797. "params": {
  798. "pause": this.playing,
  799. "hwnd": this.hwnd
  800. }
  801. }
  802. this.ws.transparent('recordPause', param)
  803. this.playing = !this.playing
  804. // 记录窗口信息
  805. let windowInfo = this.windowMap.get(this.hwnd + 1)
  806. if (windowInfo != null) {
  807. windowInfo.playing = this.playing
  808. }
  809. },
  810. /**
  811. * @method multipleRecord() 录像倍数播放
  812. */
  813. multipleRecord(command) {
  814. this.multiple = parseInt(command)
  815. let param = {
  816. "appId": this.appId,
  817. "appName": this.appName,
  818. "ctrlCode": this.ctrlCode,
  819. "params": {
  820. "multiple": this.multiple,
  821. "hwnd": this.hwnd
  822. }
  823. }
  824. this.ws.transparent('multipleRecord', param)
  825. // 记录窗口信息
  826. let windowInfo = this.windowMap.get(this.hwnd + 1)
  827. if (windowInfo != null) {
  828. windowInfo.multiple = this.multiple
  829. }
  830. },
  831. /**
  832. * @method stopPlay() 停止
  833. */
  834. stopPlay(hwnd) {
  835. let param = {
  836. "appId": this.appId,
  837. "appName": this.appName,
  838. "ctrlCode": this.ctrlCode,
  839. "params": {
  840. "hwnd": hwnd
  841. }
  842. }
  843. this.ws.transparent('stopPlay', param)
  844. },
  845. /**
  846. * @method closeAllVideo() 关闭所有
  847. */
  848. closeAllVideo() {
  849. let param = {
  850. "appId": this.appId,
  851. "appName": this.appName,
  852. "ctrlCode": this.ctrlCode,
  853. }
  854. this.ws.transparent('closeAllVideo', param)
  855. },
  856. /**
  857. * @param id 节点id
  858. * @method openNodeInfo()
  859. */
  860. openNodeInfo(id, item) {
  861. let that = this
  862. if (item) {
  863. for (var i = 0, len = that.hisStoreList.length; i < len; i++) {
  864. that.hisStoreList[i].isActive = false
  865. }
  866. item.isActive = true
  867. }
  868. Getone({ id: id }).then((res) => {
  869. let shopData = res
  870. if (!shopData.picArr || !shopData.picArr[0]) {
  871. shopData.picArr = [noShopImg]
  872. } else if (shopData.picArr[0]) {
  873. shopData.picArr[0] = that.managePicUrl(shopData.picArr[0])
  874. }
  875. // if (!shopData.storePic1) {
  876. // shopData.storePic1 = noShopImg
  877. // }
  878. that.shopData = shopData
  879. })
  880. },
  881. /**
  882. * @method initShopTree() 初始化门店树
  883. */
  884. initShopTree(onAsyncSuccess) {
  885. let that = this
  886. let searchKey = encodeURIComponent(that.searchKey)
  887. const promise = new Promise(function (resolve, reject) {
  888. getShopTree(
  889. searchKey,
  890. (isNoNode) => that.noNode = isNoNode,
  891. (node) => {
  892. console.log('videoShop clickCB')
  893. try { console.log(JSON.stringify(node)); } catch (e) { e && console.log(e.stack); }
  894. if (node.nodeType === 2) {
  895. setTimeout(function () {
  896. let userId = JSON.parse(sessionStorage.dataMsg).id
  897. if (!userId || !node) {
  898. console.log('没有用户id或者没有传入门店信息:userId:' + userId + 'store:' + node)
  899. return
  900. }
  901. let historyStoreObj = localStorage.historyStoreList ? JSON.parse(localStorage.getItem('historyStoreList')) : {}
  902. let historyStoreList = historyStoreObj[userId] || []
  903. let index = historyStoreList.indexOf(node.id)
  904. if (index !== -1) historyStoreList.splice(index, 1)
  905. historyStoreList.unshift(node.id)
  906. if (historyStoreList.length > 10) {
  907. historyStoreList = historyStoreList.splice(0, 10)
  908. }
  909. historyStoreObj[userId] = historyStoreList
  910. localStorage.historyStoreList = JSON.stringify(historyStoreObj)
  911. that.storeId = node.id
  912. that.storeName = node.name
  913. that.openNodeInfo(node.id)
  914. }, 300);
  915. }
  916. },
  917. (treeNodes) => {
  918. let [len, channelItems, devName] = [treeNodes.length, [], '']
  919. for (var i = 0; i < len; i++) {
  920. let treeNode = treeNodes[i]
  921. let channelItem = {
  922. deviceId: treeNode.deviceId,
  923. channelId: treeNode.channelId,
  924. channelName: treeNode.showName,
  925. ability: treeNode.ability,
  926. deviceModel: treeNode.deviceModel,
  927. storeId: treeNode.storeId,
  928. storeName: treeNode.storeName,
  929. accessSource: treeNode.accessSource,
  930. onlineStatus: treeNode.onlineStatus,
  931. brand: treeNode.brand,
  932. devUsername: treeNode.devUsername || '',
  933. devPassword: treeNode.devPassword || '',
  934. }
  935. channelItems.push(channelItem)
  936. }
  937. that.openVideo(channelItems)
  938. },
  939. that.$refs.videoShopTree,
  940. 'videoShopTree',
  941. onAsyncSuccess,
  942. that
  943. ).then((treeObj) => {
  944. that.treeObj = treeObj
  945. if (that.playingNodes.length > 0) {
  946. changeStatus(that.playingNodes)
  947. }
  948. onDataSuccess()
  949. resolve(true);
  950. })
  951. });
  952. return promise
  953. },
  954. /**
  955. * @method handleTreeEnter 查询树节点
  956. */
  957. handleTreeEnter(val) {
  958. this.searchKey = val
  959. this.initShopTree()
  960. },
  961. /**
  962. * @method showTab() 展示当前tab
  963. */
  964. showTab(num) {
  965. this.tabNum = num
  966. },
  967. /**
  968. * @method switchRecordType() 切换录像类型,录像类型 0:设备录像,1:云录像
  969. */
  970. switchRecordType() {
  971. this.recordType = this.recordType === 0 ? 1 : 0
  972. let windowInfo = this.windowMap.get(this.hwnd + 1)
  973. if (windowInfo != null) {
  974. windowInfo.recordType = this.recordType
  975. windowInfo.initPlay = false
  976. let startTime = `${this.dateArr[0]}-${this.addZero(this.dateArr[1])}-${this.addZero(this.dateArr[2])} 00:00:00`
  977. this.checkRecord(startTime, this.hwnd + 1)
  978. }
  979. },
  980. /**
  981. * @method timerChange() 时间点变化
  982. */
  983. timerChange(time, num, needPlayBack) {
  984. let oriTime = this.getWindowTime(num)
  985. if (oriTime == null) {
  986. oriTime = this.getNowTime()
  987. }
  988. let selectTime = `${oriTime.split(" ")[0]} ${time}`
  989. this.setWindowTime(num, selectTime)
  990. const channelInfo = this.windowMap.get(num)
  991. if (channelInfo == null) {
  992. return;
  993. } else {
  994. channelInfo.initPlay = false
  995. }
  996. if (needPlayBack) {
  997. if (this.timerChangeFn == null) {
  998. this.timerChangeFn = this.debounce((args) => {
  999. // 回放
  1000. this.playBack(args[0], args[1])
  1001. }, 500)
  1002. }
  1003. this.recordParam[num - 1].beginTime = selectTime
  1004. this.recordParam[num - 1].endTime = selectTime.split(" ")[0] + " 23:59:59"
  1005. this.timerChangeFn(this.recordData[num - 1], num - 1)
  1006. }
  1007. },
  1008. /**
  1009. * @method checkRecord() 录像查看
  1010. */
  1011. checkRecord(selectTime, num) {
  1012. const channelInfo = this.windowMap.get(num)
  1013. if (channelInfo == null) {
  1014. console.log(`分屏${num}没有绑定任何有效通道,无法查看录像`)
  1015. return false;
  1016. }
  1017. let beginTime, endTime
  1018. if (Array.isArray(selectTime)) {
  1019. beginTime = selectTime[0]
  1020. endTime = selectTime[1]
  1021. } else {
  1022. beginTime = selectTime
  1023. endTime = selectTime.split(" ")[0] + " 23:59:59"
  1024. }
  1025. this.recordParam[num - 1] = {
  1026. "token": this.lcToken,
  1027. "deviceId": channelInfo.deviceId,
  1028. "channelId": channelInfo.channelId,
  1029. "beginTime": beginTime,
  1030. "endTime": endTime,
  1031. "recordType": channelInfo.recordType,
  1032. "hwnd": num - 1,
  1033. "accessSource": channelInfo.accessSource
  1034. }
  1035. let param = {
  1036. "appId": this.appId,
  1037. "appName": this.appName,
  1038. "ctrlCode": this.ctrlCode,
  1039. "params": this.recordParam[num - 1]
  1040. }
  1041. this.ws.transparent('checkRecord', param)
  1042. },
  1043. /**
  1044. * @method playBack() 录像回放
  1045. */
  1046. playBack(recordInfos, hwnd) {
  1047. if(recordInfos.length === 0){
  1048. this.$notify({
  1049. title: '提示',
  1050. message: '没有录像',
  1051. type: 'warning',
  1052. customClass: 'fixed-bottom',
  1053. position: 'bottom-right'
  1054. });
  1055. return;
  1056. }
  1057. let params = JSON.parse(JSON.stringify(this.recordParam[hwnd]))
  1058. if (params.beginTime.indexOf("00:00:00") > -1) {
  1059. params.recordInfos = recordInfos
  1060. } else {
  1061. params.recordInfos = recordInfos.filter(record => {
  1062. const containsStart = record.strBeginTime >= params['beginTime'] && record.strBeginTime <= params['endTime']
  1063. const containsEnd = record.strEndTime >= params['beginTime'] && record.strEndTime <= params['endTime']
  1064. const containsAll = record.strEndTime >= params['endTime'] && record.strBeginTime <= params['beginTime']
  1065. return containsStart || containsEnd || containsAll
  1066. })
  1067. }
  1068. if (params.recordInfos.length === 0) {
  1069. this.$notify({
  1070. title: '提示',
  1071. message: '没有录像',
  1072. type: 'warning',
  1073. customClass: 'fixed-bottom',
  1074. position: 'bottom-right'
  1075. });
  1076. this.stopPlay(this.recordParam[hwnd])
  1077. return;
  1078. }
  1079. let windowInfo = this.windowMap.get(hwnd + 1)
  1080. params.orgCode = windowInfo.orgCode
  1081. params.orgName = windowInfo.orgName
  1082. params.devAbility = windowInfo.devAbility
  1083. params.devModel = windowInfo.devModel
  1084. params.accessSource = windowInfo.accessSource
  1085. params.channelName = windowInfo.channelName
  1086. params.recordType = windowInfo.recordType
  1087. let param = {
  1088. "appId": this.appId,
  1089. "appName": this.appName,
  1090. "ctrlCode": this.ctrlCode,
  1091. "params": params
  1092. }
  1093. this.ws.transparent('playBack', param)
  1094. },
  1095. /**
  1096. * @method prev() 上个月
  1097. */
  1098. prev() {
  1099. if (this.dateArr[1] === 1) {
  1100. this.$set(this.dateArr, '1', 12)
  1101. this.$set(this.dateArr, '0', this.dateArr[0] - 1)
  1102. } else {
  1103. this.$set(this.dateArr, '1', this.dateArr[1] - 1)
  1104. }
  1105. this.timePointClick(this.dayNum)
  1106. },
  1107. /**
  1108. * @method next() 下个月
  1109. */
  1110. next() {
  1111. if (this.dateArr[1] === 12) {
  1112. this.$set(this.dateArr, '1', 1)
  1113. this.$set(this.dateArr, '0', this.dateArr[0] + 1)
  1114. } else {
  1115. this.$set(this.dateArr, '1', this.dateArr[1] + 1)
  1116. }
  1117. this.timePointClick(this.dayNum)
  1118. },
  1119. /**
  1120. * @method timePointClick() 点击时间点
  1121. */
  1122. timePointClick(item) {
  1123. let num = this.hwnd + 1
  1124. this.$set(this.dateArr, '2', item)
  1125. this.updateSelectDate()
  1126. this.$refs['timerRef'+num][0].positionTime("12:00:00")
  1127. this.timerChange("12:00:00", num)
  1128. // 点击时间点的时候去查录像
  1129. let startTime = `${this.dateArr[0]}-${this.addZero(this.dateArr[1])}-${this.addZero(this.dateArr[2])} 00:00:00`
  1130. this.checkRecord(startTime, num)
  1131. },
  1132. /**
  1133. * @method updateSelectDate() 更新选择的日期时间
  1134. */
  1135. updateSelectDate() {
  1136. this.selectDate = `${this.dateArr[0]}-${this.addZero(this.dateArr[1])}-${this.addZero(this.dateArr[2])} 12:00:00`
  1137. this.setWindowTime(this.hwnd + 1, this.selectDate)
  1138. },
  1139. /**
  1140. * @method updateDateArr() 更新日期数组
  1141. */
  1142. updateDateArr(dateStr) {
  1143. let date = dateStr.split(" ")[0]
  1144. let time = dateStr.split(" ")[1]
  1145. let arr = date.split("-")
  1146. for (let index = 0; index < arr.length; index++) {
  1147. this.$set(this.dateArr, index, parseInt(arr[index]))
  1148. }
  1149. let num = this.hwnd + 1
  1150. this.$refs['timerRef'+num][0].positionTime(time)
  1151. let preTime = this.getWindowTime(num)
  1152. this.setWindowTime(num, dateStr)
  1153. const windowInfo = this.windowMap.get(num)
  1154. if (windowInfo != null && preTime != null && preTime.split(" ")[0] != date) {
  1155. this.timerChange(time, num)
  1156. // 切换时间的时候日期有变化才去查录像,不是每次切换都去查
  1157. let startTime = `${date} 00:00:00`
  1158. this.checkRecord(startTime, num)
  1159. } else {
  1160. this.timerChange(time, num, true)
  1161. }
  1162. },
  1163. /**
  1164. * @method dateBlur() 日期失焦
  1165. */
  1166. dateBlur() {
  1167. this.isFocus = false
  1168. this.cut([])
  1169. },
  1170. /**
  1171. * @method dateFocus() 日期聚焦
  1172. */
  1173. dateFocus() {
  1174. let cutAreaWidth = 321
  1175. let cutAreaHeight = 400
  1176. let clientWidth = this.$refs.cBox.$el.clientWidth
  1177. let clientHeight = this.$refs.cBox.$el.clientHeight
  1178. let posX = clientWidth - cutAreaWidth
  1179. let posY = clientHeight - cutAreaHeight
  1180. let pluginDialog = this.$refs.pluginDialog.$refs.dialog
  1181. let right = window.innerWidth - pluginDialog.offsetWidth - pluginDialog.offsetLeft
  1182. let cutList = [{
  1183. "posX": posX + right,
  1184. "posY": posY,
  1185. "width": cutAreaWidth,
  1186. "height": cutAreaHeight
  1187. }]
  1188. this.isFocus = true
  1189. this.cut(cutList)
  1190. },
  1191. /**
  1192. * @method dateBlur() 日期范围输入框失焦
  1193. */
  1194. dateRangeBlur() {
  1195. this.isRangeFocus = false
  1196. this.cut([this.cutList[0]])
  1197. },
  1198. /**
  1199. * @method dateFocus() 日期范围输入框聚焦
  1200. */
  1201. dateRangeFocus() {
  1202. let dialog = this.$refs.timeDownLoadDialog.$el.firstChild
  1203. this.$nextTick(() => {
  1204. let dateRangePicker = document.querySelectorAll(".el-date-range-picker")[0];
  1205. let pluginDialog = this.$refs.pluginDialog.$refs.dialog
  1206. this.cutList.push({
  1207. "posX": dateRangePicker.offsetLeft - pluginDialog.offsetLeft - parseInt(this.asideWidth),
  1208. "posY": dateRangePicker.offsetTop - pluginDialog.offsetTop - 54,
  1209. "width": dateRangePicker.clientWidth,
  1210. "height": dateRangePicker.clientHeight
  1211. })
  1212. this.isRangeFocus = true
  1213. this.cut(this.cutList)
  1214. });
  1215. },
  1216. /**
  1217. * @method queryVersion() 查询客户端版本,只是为了建立连接,没什么特殊意义
  1218. */
  1219. queryVersion() {
  1220. let param = {
  1221. "appId": this.appId,
  1222. "appName": this.appName,
  1223. "ctrlCode": this.ctrlCode,
  1224. }
  1225. this.ws.transparent('checkVersion', param)
  1226. },
  1227. /**
  1228. * @method recordDateChange() 日期变化查询录像文件
  1229. */
  1230. recordDateChange(date) {
  1231. if (date) {
  1232. this.needPlayBack = false
  1233. this.checkRecord(date + " 00:00:00", this.hwnd + 1)
  1234. }
  1235. },
  1236. /**
  1237. * @method openDownloadDialog() 打开下载选择文件的弹框
  1238. */
  1239. openDownloadDialog() {
  1240. // 关闭下载模式选择框
  1241. this.showDownloadMode = false
  1242. // 置为无需回放
  1243. this.needPlayBack = false
  1244. let windowTime = this.getWindowTime(this.hwnd + 1)
  1245. if (windowTime != null) {
  1246. this.checkRecord(windowTime.split(" ")[0] + " 00:00:00", this.hwnd + 1)
  1247. this.recordDate = windowTime.split(" ")[0]
  1248. // 打开文件下载框
  1249. this.downLoadDialogVisible = true
  1250. }
  1251. },
  1252. /**
  1253. * @method downLoadByFile() 录像下载-按文件
  1254. */
  1255. downLoadByFile(row, index) {
  1256. let windowInfo = this.windowMap.get(this.hwnd + 1)
  1257. let params = {
  1258. "token": this.lcToken,
  1259. "deviceId": windowInfo.deviceId,
  1260. "channelId": windowInfo.channelId,
  1261. "recordType": windowInfo.recordType,
  1262. "devAbility": windowInfo.devAbility,
  1263. "devModel": windowInfo.devModel,
  1264. "channelName": windowInfo.channelName,
  1265. "shopName" : windowInfo.orgName,
  1266. "recordInfo": row
  1267. }
  1268. let data = {
  1269. "appId": this.appId,
  1270. "appName": this.appName,
  1271. "ctrlCode": this.ctrlCode,
  1272. "params": params
  1273. }
  1274. this.recordData[this.hwnd].splice(index, 1);
  1275. this.ws.transparent('startDownloadByFile', data)
  1276. },
  1277. /**
  1278. * @method toggleMode() 点击外部区域关闭下载模式选择面板
  1279. */
  1280. toggleMode(e) {
  1281. if (this.showDownloadMode && e.target.id !== 'record-download-span') {
  1282. this.showDownloadMode = false
  1283. }
  1284. },
  1285. /**
  1286. * @method openByTimeDownloadDialog() 打开录像下载弹框-按时间
  1287. */
  1288. openByTimeDownloadDialog() {
  1289. // 关闭下载模式选择框
  1290. this.showDownloadMode = false
  1291. // 打开时间下载弹框
  1292. this.timeDownLoadDialogVisible = true
  1293. },
  1294. /**
  1295. * @method downLoadByTime() 录像下载-按时间
  1296. */
  1297. downLoadByTime() {
  1298. this.$refs['downloadForm'].validate((valid) => {
  1299. if (valid) {
  1300. // 置为无需回放
  1301. this.needPlayBack = false
  1302. // 关闭时间下载弹框
  1303. this.timeDownLoadDialogVisible = false
  1304. this.byTimeDownLoad = true
  1305. this.checkRecord(this.downloadForm.timeRangeValue, this.hwnd + 1)
  1306. }
  1307. });
  1308. },
  1309. /**
  1310. * @method startDownloadByTime() 开始录像下载-按时间
  1311. */
  1312. startDownloadByTime(recordInfos) {
  1313. let windowInfo = this.windowMap.get(this.hwnd + 1)
  1314. let params = {
  1315. "token": this.lcToken,
  1316. "deviceId": windowInfo.deviceId,
  1317. "channelId": windowInfo.channelId,
  1318. "beginTime": this.recordParam[this.hwnd]['beginTime'],
  1319. "endTime": this.recordParam[this.hwnd]['endTime'],
  1320. "recordType": windowInfo.recordType,
  1321. "devAbility": windowInfo.devAbility,
  1322. "devModel": windowInfo.devModel,
  1323. "channelName": windowInfo.channelName,
  1324. "shopName" : windowInfo.orgName,
  1325. "recordInfos": recordInfos
  1326. }
  1327. let data = {
  1328. "appId": this.appId,
  1329. "appName": this.appName,
  1330. "ctrlCode": this.ctrlCode,
  1331. "params": params
  1332. }
  1333. this.ws.transparent('startDownloadByTime', data)
  1334. },
  1335. /**
  1336. * @method cutDownloadDialog() 裁剪下载弹框
  1337. */
  1338. cutDownloadDialog() {
  1339. let dialog = this.$refs.downLoadDialog.$el.firstChild
  1340. let clientWidth = dialog.clientWidth
  1341. let clientHeight = 529
  1342. let pluginDialog = this.$refs.pluginDialog.$refs.dialog
  1343. let cutList = [{
  1344. "posX": dialog.offsetLeft - pluginDialog.offsetLeft - parseInt(this.asideWidth),
  1345. "posY": dialog.offsetTop - pluginDialog.offsetTop - 54,
  1346. "width": clientWidth,
  1347. "height": clientHeight
  1348. }]
  1349. this.cut(cutList)
  1350. },
  1351. /**
  1352. * @method cutTimeDownloadDialog() 裁剪按时间下载弹框
  1353. */
  1354. cutTimeDownloadDialog() {
  1355. let dialog = this.$refs.timeDownLoadDialog.$el.firstChild
  1356. let clientWidth = dialog.clientWidth
  1357. let clientHeight = dialog.clientHeight
  1358. let pluginDialog = this.$refs.pluginDialog.$refs.dialog
  1359. this.cutList = [{
  1360. "posX": dialog.offsetLeft - pluginDialog.offsetLeft - parseInt(this.asideWidth),
  1361. "posY": dialog.offsetTop - pluginDialog.offsetTop - 54,
  1362. "width": clientWidth,
  1363. "height": clientHeight
  1364. }]
  1365. this.cut(this.cutList)
  1366. },
  1367. /**
  1368. * @method setWindowTime() 设置窗口时间
  1369. */
  1370. setWindowTime(num, time) {
  1371. if (this.windowMap.get(num)) {
  1372. this.windowMap.get(num)['time'] = time
  1373. }
  1374. },
  1375. /**
  1376. * @method getWindowTime() 获取窗口时间
  1377. */
  1378. getWindowTime(num) {
  1379. if (this.windowMap.get(num)) {
  1380. return this.windowMap.get(num)['time']
  1381. } else {
  1382. return null
  1383. }
  1384. },
  1385. /**
  1386. * @method addZero() 月日时分秒补0函数
  1387. */
  1388. addZero: (time) => {
  1389. let newTime = time > 9 ? time : '0' + time
  1390. return newTime
  1391. },
  1392. /**
  1393. * 获取格式为yyyy-mm-dd hh:mm:ss的日期
  1394. */
  1395. getNowTime() {
  1396. const time = {}
  1397. let date = new Date()
  1398. time.y = this.addZero(date.getFullYear())
  1399. time.m = this.addZero(date.getMonth() + 1)
  1400. time.d = this.addZero(date.getDate())
  1401. time.h = this.addZero(date.getHours())
  1402. time.min = this.addZero(date.getMinutes())
  1403. time.s = this.addZero(date.getSeconds())
  1404. return `${time.y}-${time.m}-${time.d} ${time.h}:${time.min}:${time.s}`
  1405. },
  1406. /**
  1407. * @method downloadTip() 下载返回的提示
  1408. */
  1409. downloadTip(res, message = '下载中,请稍候...') {
  1410. if (res.success) {
  1411. this.$notify({
  1412. title: '提示',
  1413. message: message,
  1414. type: 'warning',
  1415. position: 'bottom-right'
  1416. });
  1417. } else {
  1418. this.$notify({
  1419. title: '提示',
  1420. message: res.dataObj,
  1421. type: 'warning',
  1422. position: 'bottom-right'
  1423. });
  1424. }
  1425. },
  1426. /**
  1427. * 监听客户端消息
  1428. */
  1429. onClientMessage() {
  1430. // 监控注册状态
  1431. this.ws.on("registerState", (res) => {
  1432. if (res && res.appName === this.appName) {
  1433. this.isLogin = res.params.registerResult === 0;
  1434. if (this.isLogin) {
  1435. this.appId = res.appId
  1436. console.log("注册成功:" + JSON.stringify(res));
  1437. this.create()
  1438. } else {
  1439. console.log("注册失败");
  1440. }
  1441. }
  1442. });
  1443. // 监控激活窗口变化情况
  1444. this.ws.on("activeWnd", (res) => {
  1445. if (res && res.ctrlCode === this.ctrlCode && res.appId === this.appId && res.appName === this.appName) {
  1446. let dataObj = JSON.parse(res.dataObj)
  1447. if (this.hwnd !== dataObj.activeWnd) {
  1448. // 默认是静音状态
  1449. this.audioing = false
  1450. }
  1451. this.channelId = dataObj.channelID + ""
  1452. this.hwnd = dataObj.activeWnd
  1453. this.definitionMode = dataObj.definitionMode
  1454. this.deviceId = dataObj.deviceID
  1455. let time = this.getWindowTime(this.hwnd + 1)
  1456. if (time != null) {
  1457. this.selectDate = time
  1458. let times = time.split(" ")[0].split("-")
  1459. for (let index = 0; index < times.length; index++) {
  1460. this.$set(this.dateArr, index, parseInt(times[index]))
  1461. }
  1462. }
  1463. // 窗口变化时,变化对应的录像类型、播放状态、倍数
  1464. let windowInfo = this.windowMap.get(this.hwnd + 1)
  1465. if (windowInfo != null) {
  1466. this.recordType = windowInfo.recordType
  1467. this.multiple = windowInfo.multiple
  1468. this.playing = windowInfo.playing
  1469. this.recording = windowInfo.recording
  1470. this.talking = windowInfo.talking
  1471. this.brand = windowInfo.brand
  1472. } else {
  1473. this.recordType = 0
  1474. this.multiple = 1
  1475. this.playing = true
  1476. this.recording = false //默认是非录像状态
  1477. this.talking = false //默认是非对讲状态
  1478. this.brand = 'general'
  1479. }
  1480. }
  1481. });
  1482. //监听查看录像的返回结果
  1483. this.ws.on("checkRecordReply", (res) => {
  1484. if (res && res.ctrlCode === this.ctrlCode && res.appId === this.appId && res.appName === this.appName) {
  1485. if (res.recordInfos.length === 0) {
  1486. this.$notify({
  1487. title: '提示',
  1488. message: '没有录像',
  1489. type: 'warning',
  1490. customClass: 'fixed-bottom',
  1491. position: 'bottom-right'
  1492. });
  1493. let windowInfo = this.windowMap.get(res.hwnd + 1)
  1494. if ((windowInfo != null && windowInfo.recordType === 1) || (windowInfo != null && !windowInfo.initPlay && !this.byTimeDownLoad)) {
  1495. // 没有录像要停止窗口的播放
  1496. this.stopPlay(res.hwnd)
  1497. this.$set(this.recordInfoObj, res.hwnd + 1, [])
  1498. }
  1499. if (!this.byTimeDownLoad) {
  1500. this.recordData[res.hwnd] = []
  1501. } else {
  1502. this.byTimeDownLoad = false
  1503. }
  1504. this.needPlayBack = true
  1505. return false
  1506. }
  1507. let hwnd = res.hwnd
  1508. let recordInfos = res.recordInfos
  1509. let windowTime = this.getWindowTime(hwnd + 1)
  1510. recordInfos.forEach(record => {
  1511. record.windowTime = windowTime;
  1512. })
  1513. // 按时间下载
  1514. if (this.byTimeDownLoad) {
  1515. this.startDownloadByTime(res.recordInfos)
  1516. this.byTimeDownLoad = false
  1517. } else {
  1518. this.$set(this.recordData, hwnd, recordInfos)
  1519. if (!this.downLoadDialogVisible) {
  1520. // 传值录像片段给timer组件,设置录像片段背景
  1521. this.$set(this.recordInfoObj, hwnd + 1, recordInfos)
  1522. }
  1523. }
  1524. if (this.needPlayBack) {
  1525. // 回放
  1526. this.playBack(recordInfos, hwnd)
  1527. } else {
  1528. this.needPlayBack = true
  1529. }
  1530. }
  1531. });
  1532. // 监听开始下载的返回结果
  1533. this.ws.on("startDownloadByFile", (res) => {
  1534. if (res && res.ctrlCode === this.ctrlCode && res.appId === this.appId && res.appName === this.appName) {
  1535. this.downloadTip(res)
  1536. }
  1537. });
  1538. this.ws.on("startDownloadByTime", (res) => {
  1539. if (res && res.ctrlCode === this.ctrlCode && res.appId === this.appId && res.appName === this.appName) {
  1540. this.downloadTip(res)
  1541. }
  1542. });
  1543. // 监听关闭单个窗口(停止)的返回结果
  1544. this.ws.on("stopPlay", (res) => {
  1545. if (res && res.ctrlCode === this.ctrlCode && res.appId === this.appId && res.appName === this.appName && res.success) {
  1546. // 播放状态 0.播放关闭 1.实时播放成功,2.录像播放成功,3.实时播放失败,4.录像播放失败
  1547. let windowInfo = this.windowMap.get(JSON.parse(res.dataObj).hwnd + 1)
  1548. windowInfo['playStatus'] = 0
  1549. this.$forceUpdate();
  1550. // 修改通道状态图标
  1551. let param = {deviceID: windowInfo.deviceId, channelID: windowInfo.channelId, status: 0}
  1552. this.playingNodes = this.playingNodes.filter(item => !(item.deviceID === windowInfo.deviceId && item.channelID === windowInfo.channelId))
  1553. changeStatus([param])
  1554. }
  1555. });
  1556. // 监听关闭所有窗口的返回结果
  1557. this.ws.on("closeAllVideo", (res) => {
  1558. if (res && res.ctrlCode === this.ctrlCode && res.appId === this.appId && res.appName === this.appName && res.success) {
  1559. let statusParam = []
  1560. this.windowMap.forEach((windowInfo, key) => {
  1561. windowInfo['playStatus'] = 0
  1562. this.$forceUpdate();
  1563. // 修改通道状态图标
  1564. let param = {deviceID: windowInfo.deviceId, channelID: windowInfo.channelId, status: 0}
  1565. statusParam.push(param)
  1566. })
  1567. this.playingNodes = []
  1568. changeStatus(statusParam)
  1569. }
  1570. });
  1571. // 监听下载成功失败的回复
  1572. this.ws.on("downloadStatusNotify", (res) => {
  1573. if (res && res.ctrlCode === this.ctrlCode && res.appId === this.appId && res.appName === this.appName) {
  1574. if (res.success) {
  1575. this.downloadTip(res, "下载成功,请前往客户端安装目录的Download文件夹查看")
  1576. } else {
  1577. res.dataObj = "下载失败"
  1578. this.downloadTip(res)
  1579. }
  1580. }
  1581. });
  1582. // 监控播放状态返回
  1583. this.ws.on("videoStatusNotify", (res) => {
  1584. if (res && res.ctrlCode === this.ctrlCode && res.appId === this.appId && res.appName === this.appName) {
  1585. let dataObj = JSON.parse(res.dataObj)
  1586. let hwnd = dataObj.hwnd
  1587. // 播放状态 0.播放关闭 1.实时播放成功,2.录像播放成功,3.实时播放失败,4.录像播放失败
  1588. let windowInfo = this.windowMap.get(hwnd + 1)
  1589. windowInfo['playStatus'] = dataObj.playStatus
  1590. this.$forceUpdate();
  1591. // 修改通道状态图标
  1592. let param = {deviceID: windowInfo.deviceId, channelID: windowInfo.channelId}
  1593. param.status = [1,2].includes(dataObj.playStatus) ? 1 : 0
  1594. this.playingNodes.push(param)
  1595. changeStatus([param])
  1596. if (dataObj.playStatus === 1) {
  1597. // 实时播放成功,查询当天的录像
  1598. let nowTime = this.getNowTime()
  1599. let nowDate = nowTime.split(" ")[0]
  1600. this.needPlayBack = false
  1601. this.setWindowTime(hwnd + 1, `${nowDate} 12:00:00`)
  1602. this.checkRecord(`${nowDate} 00:00:00`, hwnd + 1)
  1603. }
  1604. }
  1605. });
  1606. },
  1607. /**
  1608. * 获取开放平台token
  1609. */
  1610. getToken() {
  1611. let that = this
  1612. // const url = `${config.base_url}/gateway/auth/oauth/token?grant_type=client_credentials&scope=server&client_id=${config.client_id}&client_secret=${config.client_secret}`
  1613. const url = `${config.base_url}/admin/dahua/token`;
  1614. axios({
  1615. headers: {
  1616. 'Content-Type': 'application/x-www-form-urlencoded'
  1617. },
  1618. method: 'POST',
  1619. url: url,
  1620. }).then(res => {
  1621. if (res.data.access_token) {
  1622. sessionStorage.setItem('token', res.data.access_token)
  1623. that.initShopTree().then(function () {
  1624. that.showNarrow = true;
  1625. })
  1626. }
  1627. })
  1628. },
  1629. getOffsetTopByBody (el) {
  1630. let offsetTop = 0
  1631. while (el && el.tagName !== 'BODY') {
  1632. offsetTop += el.offsetTop
  1633. el = el.offsetParent
  1634. }
  1635. return offsetTop
  1636. },
  1637. // 打开插件弹框
  1638. open() {
  1639. this.pluginDialogVisible = true
  1640. this.$nextTick(() => {
  1641. let dialog = this.$refs.pluginDialog.$refs.dialog
  1642. this.ctrlPosY = dialog.offsetTop + 54
  1643. this.ctrlPosX = dialog.offsetLeft + 205
  1644. this.init()
  1645. });
  1646. },
  1647. // 弹框初始化
  1648. init() {
  1649. // 获取开放平台token
  1650. this.getToken();
  1651. // 窗口变化时重新定位视频插件,重新裁剪
  1652. window.addEventListener('resize', this.resizeFn = this.debounce(() => {
  1653. if (this.$refs.pluginDialog) {
  1654. let dialog = this.$refs.pluginDialog.$refs.dialog
  1655. this.ctrlPosX = dialog.offsetLeft + 205
  1656. this.ctrlPosY = dialog.offsetTop + 54
  1657. }
  1658. this.setPos()
  1659. if (this.downLoadDialogVisible) {
  1660. this.cutDownloadDialog()
  1661. }
  1662. if (this.timeDownLoadDialogVisible) {
  1663. this.cutTimeDownloadDialog()
  1664. }
  1665. if (this.isFocus) {
  1666. this.dateFocus()
  1667. }
  1668. if (this.isRangeFocus) {
  1669. this.dateRangeFocus()
  1670. }
  1671. }, 50))
  1672. // 连接websocket
  1673. this.connect()
  1674. if (this.isLogin) {
  1675. this.create()
  1676. } else {
  1677. this.register()
  1678. }
  1679. // 监听客户端返回的消息
  1680. this.onClientMessage();
  1681. // 回放模块的日期默认为当前日期
  1682. let now = new Date();
  1683. // [年, 月, 日]
  1684. this.dateArr = [now.getFullYear(), now.getMonth() + 1, now.getDate()]
  1685. this.updateSelectDate()
  1686. this.playingNodes = []
  1687. },
  1688. /**
  1689. * 防抖函数
  1690. */
  1691. debounce(fn, time, timer) {
  1692. var timer = timer || null
  1693. return function () {
  1694. let args = arguments
  1695. clearTimeout(timer)
  1696. timer = setTimeout(() => {
  1697. fn.call(this, args)
  1698. }, time)
  1699. }
  1700. },
  1701. },
  1702. created() {
  1703. const random = Math.ceil((1 + Math.random()) * 100000000)
  1704. this.ctrlCode = 'ctrlBox' + random
  1705. this.appName = 'appBox' + random
  1706. },
  1707. deactivated() {
  1708. this.unregister()
  1709. window.removeEventListener('resize', this.resizeFn)
  1710. }
  1711. })
  1712. </script>
  1713. </html>