demo.php 76 KB

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