demo.html 76 KB

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