camera.html 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
  6. <meta http-equiv="Pragma" content="no-cache"/>
  7. <meta http-equiv="Cache-Control" content="no-cache, must-revalidate"/>
  8. <meta http-equiv="Expires" content="0"/>
  9. <title>数据大屏:数据大屏教学现场</title>
  10. <link rel="stylesheet" href="demo1.css">
  11. <link rel="stylesheet" href="./css/style.css">
  12. <script>
  13. document.write(
  14. "<link type='text/css' href='./demo.css?version=" +
  15. new Date().getTime() +
  16. "' rel='stylesheet' />",
  17. );
  18. </script>
  19. <script src="./vue.js"></script>
  20. </head>
  21. <body>
  22. <div class="main">
  23. <div class="main-header">
  24. <div class="sy"
  25. onclick="redirectToUrl('http://172.16.0.63:28080/visualpro/preview.html?id=003fe9f6a92b47bc924fe04f59fd6acb')">
  26. <div>首页</div>
  27. </div>
  28. <div class="jyjx"
  29. onclick="redirectToUrl('http://172.16.0.63:28080/visualpro/preview.html?id=7f0176157e93423e9b4e48cae1c186f3')">
  30. <div>教育教学</div>
  31. </div>
  32. <div class="zsxx"
  33. onclick="redirectToUrl('http://172.16.0.63:28080/visualpro/preview.html?id=e67784d9632c44099e03b56256df7e5d')">
  34. <div>招生信息</div>
  35. </div>
  36. <div class="jyxx"
  37. onclick="redirectToUrl('http://172.16.0.63:28080/visualpro/preview.html?id=22a2c238d23e4eb0a80bd2e4f047ba90')">
  38. <div>就业信息</div>
  39. </div>
  40. <div class="zwhq"
  41. onclick="redirectToUrl('http://172.16.0.63:28080/visualpro/preview.html?id=2a864f38d2964ebdb63db7257a48c330')">
  42. <div>总务后勤</div>
  43. </div>
  44. <div class="jsxx"
  45. onclick="redirectToUrl('http://172.16.0.63:28080/visualpro/preview.html?id=83f18d8a43a24be79a041e4cec596fa3')">
  46. <div>教师信息</div>
  47. </div>
  48. <div class="xsxx"
  49. onclick="redirectToUrl('http://172.16.0.63:28080/visualpro/preview.html?id=5f656466368442ee88abfe34a42a3dd8')">
  50. <div>学生信息</div>
  51. </div>
  52. <div class="shfw"
  53. onclick="redirectToUrl('http://172.16.0.63:28080/visualpro/preview.html?id=170f4fae167d4b2eb7578073cb713fb1')">
  54. <div>社会服务</div>
  55. </div>
  56. <div class="jxxc">
  57. <div>教学现场</div>
  58. </div>
  59. </div>
  60. <div class="main-container">
  61. <div class="menu-container" id="menu-container">
  62. <div class="campus-tabs">
  63. <div class="campus-tab" id="xqxq-tab" onclick="switchCampus('西溪校区')">
  64. <div>西溪校区</div>
  65. </div>
  66. <div class="campus-tab campus-tab-active" id="laxq-tab" onclick="switchCampus('临安校区')">
  67. <div>临安校区</div>
  68. </div>
  69. </div>
  70. <div class="title-item">摄像头列表</div>
  71. <div v-for="(item,index) in message" :key="index">
  72. <div class="menu-title">
  73. <img src="./icon%202.png" alt=""> <span>{{item.buildname}}</span>
  74. </div>
  75. <div class="class-container">
  76. <div class="class-item" v-for="(item1,index1) in item.classes" :key="index1"
  77. @click="handleClick(item1)">
  78. <div>
  79. <img src="./03.png" alt="">
  80. </div>
  81. <div>{{item1.classname}}</div>
  82. </div>
  83. </div>
  84. </div>
  85. </div>
  86. <div class="video-container">
  87. <div class="hikvision-block">
  88. <div class="title-item video-title">
  89. <span>视频区域-室内</span>
  90. <select class="sel2" onchange="changeWndNum(this.value);">
  91. <option value="1" selected>1x1</option>
  92. <option value="2">2x2</option>
  93. <option value="3">3x3</option>
  94. <option value="4">4x4</option>
  95. <option value="1*2">1x2</option>
  96. <option value="2*1">2x1</option>
  97. </select>
  98. </div>
  99. <div class="hikvision-body">
  100. <div id="divPlugin" class="plugin"></div>
  101. <div style="display:none;">
  102. <input id="loginip" type="text" class="txt" value=""/>
  103. <input id="port" type="text" class="txt" value=""/>
  104. <input id="username" type="text" class="txt" value=""/>
  105. <input id="password" type="password" class="txt" value=""/>
  106. <input id="deviceport" type="text" class="txt"/>
  107. <input id="rtspport" type="text" class="txt"/>
  108. <select id="ip" class="sel" onchange="getChannelInfo();getDevicePort();"></select>
  109. <select id="channels" class="sel"></select>
  110. <select id="streamtype" class="sel">
  111. <option value="1" selected>主码流</option>
  112. <option value="2">子码流</option>
  113. <option value="3">第三码流</option>
  114. <option value="4">转码码流</option>
  115. </select>
  116. </div>
  117. </div>
  118. </div>
  119. <div class="dahua-block">
  120. <div class="title-item video-title">
  121. <span>视频区域-户外</span>
  122. </div>
  123. <div class="container" id="dahua-player-shell">
  124. <div class="tab">
  125. <span style="display:none;" id="device_ipc_tab">IPC/SD</span>
  126. <label style="display:none;" t="com.Language">Language</label>
  127. <select id="onChangeLanguage" style="width:80px;display:none;">
  128. <option value="SimpChinese">中文</option>
  129. <option value="English">English</option>
  130. </select>
  131. </div>
  132. <div class="content" id="device_ipc_content">
  133. <div class="h5-left">
  134. <ul style="display:none;" class="h5-menu-list">
  135. <li data-ip="10.10.43.220" data-port="80" data-user="admin" data-pswd="l1234567">
  136. <span class="deviceName" t="com.Device+ 1"></span>
  137. <button type="button" class="testButton" t="com.Test"></button>
  138. </li>
  139. <li data-ip="10.10.43.219" data-port="80" data-user="admin" data-pswd="l1234567">
  140. <span class="deviceName" t="com.Device+ 1"></span>
  141. <button type="button" class="testButton" t="com.Test"></button>
  142. </li>
  143. <li data-ip="10.10.44.5" data-port="80" data-user="admin" data-pswd="l1234567">
  144. <span class="deviceName" t="com.Device+ 1"></span>
  145. <button type="button" class="testButton" t="com.Test"></button>
  146. </li>
  147. </ul>
  148. </div>
  149. <div class="h5-left">
  150. <div style="text-align: right; margin: 0 0 8px 0;">
  151. <select class="h5-select" sel-for="onChangeWdnNum" style="width:80px;">
  152. <option value="1" selected>1x1</option>
  153. <option value="2">2x2</option>
  154. <option value="3">3x3</option>
  155. <option value="4">4x4</option>
  156. </select>
  157. </div>
  158. <div class="h5-play-wrap" style="position: relative;">
  159. </div>
  160. <select id="h5_stream" sel-for="onChangeStream" style="display:none;"></select>
  161. <select id="h5_channel" sel-for="onChangeChannel" style="display:none;"></select>
  162. <select id="h5_ivs" style="display:none;">
  163. <option value="0" t="com.No"></option>
  164. <option value="1" t="com.Yes"></option>
  165. </select>
  166. <input type="range" id="h5_volume" min="0" max="1" step="0.1" value="0.5" style="display:none;">
  167. <span id="h5_volume_value" style="display:none;"></span>
  168. <!-- 隐藏但保留,供脚本使用 -->
  169. <select id="h5_playback_channel" style="display:none;"></select>
  170. <input type="datetime-local" id="h5_startTime" style="display:none;">
  171. <input type="datetime-local" id="h5_endTime" style="display:none;">
  172. <table id="h5_table" style="display:none;"><tbody></tbody></table>
  173. <input type="checkbox" id="h5_checkAll" style="display:none;">
  174. <input type="text" id="h5_goNumber" style="display:none;">
  175. <input type="button" id="h5_goPage" style="display:none;">
  176. <input type="text" id="h5_goTime" style="display:none;">
  177. <input type="text" id="h5_cutStartTime" style="display:none;">
  178. <input type="text" id="h5_cutEndTime" style="display:none;">
  179. <input type="button" btn-for="onStartCut" style="display:none;">
  180. <span id="h5_down_process" style="display:none;"></span>
  181. <span id="h5_curTime" style="display:none;"></span>
  182. <span id="h5_totalTime" style="display:none;"></span>
  183. <span id="h5_cut_process" style="display:none;"></span>
  184. <a id="h5_first" style="display:none;"></a>
  185. <a id="h5_pre" style="display:none;"></a>
  186. <a id="h5_next" style="display:none;"></a>
  187. <a id="h5_last" style="display:none;"></a>
  188. <span id="h5_curPage" style="display:none;">1</span>
  189. <span id="h5_totalPage" style="display:none;">1</span>
  190. </div>
  191. </div>
  192. </div>
  193. </div>
  194. </div>
  195. </div>
  196. </div>
  197. <script src="./jquery-1.7.1.min.js"></script>
  198. <script id="videonode" src="./codebase/webVideoCtrl.js"></script>
  199. <script src="./cn/demo.js"></script>
  200. <!-- 仅保留大华播放依赖 -->
  201. <script>
  202. </script>
  203. <script src="./module/PlayerControl.js"></script>
  204. <script src="./module/index.js"></script>
  205. <script>
  206. // 清理大华demo中的默认onload提示,防止关闭页面
  207. window.onload = null;
  208. // 全局错误处理:捕获 WebAssembly 内存错误
  209. window.addEventListener('error', function(event) {
  210. if (event.error && event.error.message &&
  211. (event.error.message.indexOf('Out of memory') !== -1 ||
  212. event.error.message.indexOf('WebAssembly') !== -1)) {
  213. console.error('[MEMORY ERROR] WebAssembly内存不足:', event.error);
  214. // 可以在这里添加用户提示或自动清理逻辑
  215. }
  216. });
  217. // 捕获未处理的 Promise 拒绝(WebAssembly 错误通常在这里)
  218. window.addEventListener('unhandledrejection', function(event) {
  219. if (event.reason && event.reason.message &&
  220. (event.reason.message.indexOf('Out of memory') !== -1 ||
  221. event.reason.message.indexOf('WebAssembly') !== -1)) {
  222. console.error('[MEMORY ERROR] WebAssembly内存不足 (Promise):', event.reason);
  223. event.preventDefault(); // 阻止默认的错误处理
  224. // 检查当前分屏模式
  225. var wndSel = document.querySelector('#device_ipc_content select[sel-for="onChangeWdnNum"]');
  226. var currentMode = wndSel ? parseInt(wndSel.value) : 1;
  227. var maxWindows = currentMode * currentMode;
  228. var errorMsg = '检测到内存不足错误。\n\n';
  229. if (maxWindows > 4) {
  230. errorMsg += '当前使用 ' + currentMode + 'x' + currentMode + ' 分屏模式(' + maxWindows + '个窗口),内存占用较高。\n\n';
  231. errorMsg += '建议:\n1. 切换到2x2分屏模式(4个窗口)\n2. 或刷新页面释放内存';
  232. } else {
  233. errorMsg += '建议刷新页面释放内存。';
  234. }
  235. if (confirm(errorMsg + '\n\n是否现在刷新页面?')) {
  236. window.location.reload();
  237. }
  238. }
  239. });
  240. // 监控内存使用情况(如果浏览器支持)
  241. if (performance && performance.memory) {
  242. setInterval(function() {
  243. var memory = performance.memory;
  244. var usedMB = (memory.usedJSHeapSize / 1048576).toFixed(2);
  245. var totalMB = (memory.totalJSHeapSize / 1048576).toFixed(2);
  246. var limitMB = (memory.jsHeapSizeLimit / 1048576).toFixed(2);
  247. var usagePercent = ((memory.usedJSHeapSize / memory.jsHeapSizeLimit) * 100).toFixed(2);
  248. // 如果内存使用超过80%,发出警告
  249. if (usagePercent > 80) {
  250. console.warn('[MEMORY WARNING] 内存使用率: ' + usagePercent + '% (' + usedMB + 'MB / ' + limitMB + 'MB)');
  251. }
  252. }, 10000); // 每10秒检查一次
  253. }
  254. </script>
  255. <script>
  256. function redirectToUrl(url) {
  257. // console.log(url);
  258. window.location.href = url;
  259. }
  260. function isDahuaBrand(brand) {
  261. if (!brand) return false;
  262. var b = (brand + '').toLowerCase();
  263. return b.indexOf('dahua') !== -1 || b.indexOf('大华') !== -1;
  264. }
  265. function isHikvisionBrand(brand) {
  266. if (!brand) return false;
  267. var b = (brand + '').toLowerCase();
  268. return b.indexOf('hik') !== -1 || b.indexOf('海康') !== -1 || b.indexOf('hikvision') !== -1;
  269. }
  270. function playDahuaCamera(cameraInfo) {
  271. console.log('[DAHUA] playDahuaCamera input:', cameraInfo);
  272. var dahuaDeviceList = document.querySelector('#device_ipc_content .h5-menu-list li');
  273. if (!dahuaDeviceList) {
  274. console.error('未找到大华摄像头播放容器');
  275. return;
  276. }
  277. // 检查当前分屏模式,支持多窗口播放
  278. var wndSel = document.querySelector('#device_ipc_content select[sel-for="onChangeWdnNum"]');
  279. var currentMode = wndSel ? parseInt(wndSel.value) : 1;
  280. var maxWindows = currentMode * currentMode; // 1x1=1, 2x2=4, 3x3=9, 4x4=16
  281. console.log('[DAHUA] Current window mode:', currentMode + 'x' + currentMode + ' (' + maxWindows + ' windows)');
  282. // 优化内存管理:只清理当前要播放的窗口,而不是所有窗口
  283. // 这样可以支持多窗口同时播放
  284. try {
  285. // 检查是否有太多窗口在播放,如果超过限制则提示
  286. var playWrap = document.querySelector('#device_ipc_content .h5-play-wrap');
  287. if (playWrap) {
  288. var activePlayers = playWrap.querySelectorAll('div[wnd-index][style*="display: block"]');
  289. var activeCount = activePlayers.length;
  290. // 如果当前模式需要更多窗口,检查内存是否足够
  291. if (maxWindows > 4 && activeCount >= 4) {
  292. console.warn('[DAHUA] Warning: Multiple windows detected. Memory usage may be high.');
  293. }
  294. }
  295. } catch(e) {
  296. console.warn('[DAHUA] Error checking active players:', e);
  297. }
  298. // 等待一小段时间确保DOM更新完成
  299. setTimeout(function() {
  300. try {
  301. // 设置摄像头信息
  302. dahuaDeviceList.setAttribute('data-ip', cameraInfo.ip || '');
  303. dahuaDeviceList.setAttribute('data-port', cameraInfo.port || '80');
  304. dahuaDeviceList.setAttribute('data-user', cameraInfo.username || 'admin');
  305. dahuaDeviceList.setAttribute('data-pswd', cameraInfo.password || '');
  306. var nameSpan = dahuaDeviceList.querySelector('.deviceName');
  307. if (nameSpan) {
  308. nameSpan.innerText = cameraInfo.classname || '摄像头';
  309. }
  310. // 触发播放
  311. var testBtn = dahuaDeviceList.querySelector('.testButton');
  312. if (testBtn) {
  313. console.log('[DAHUA] Triggering playback for window mode:', currentMode + 'x' + currentMode);
  314. testBtn.click();
  315. }
  316. // 如果用户选择了多窗口模式,保持该模式
  317. // 不需要强制改为单窗口,支持多窗口播放
  318. if (wndSel && typeof onChangeWdnNum === 'function') {
  319. // 确保窗口布局正确
  320. onChangeWdnNum();
  321. }
  322. } catch(e) {
  323. console.error('[DAHUA] Error playing camera:', e);
  324. if (e.message && (e.message.indexOf('Out of memory') !== -1 ||
  325. e.message.indexOf('WebAssembly') !== -1)) {
  326. var errorMsg = '内存不足,无法播放视频。\n\n';
  327. if (maxWindows > 4) {
  328. errorMsg += '建议:\n1. 切换到2x2或更少的分屏模式\n2. 刷新页面释放内存\n3. 使用子码流(降低分辨率)';
  329. } else {
  330. errorMsg += '建议:\n1. 刷新页面释放内存\n2. 减少同时播放的摄像头数量';
  331. }
  332. alert(errorMsg);
  333. }
  334. }
  335. }, 100); // 减少延迟,提高响应速度
  336. }
  337. function playHikvisionCamera(cameraInfo) {
  338. console.log('[HIKVISION] playHikvisionCamera input:', cameraInfo);
  339. if (!cameraInfo.ip) {
  340. console.error('摄像头IP地址为空,无法登录海康');
  341. alert('摄像头信息不完整,无法登录海康摄像头');
  342. return;
  343. }
  344. clickLogout();
  345. setTimeout(function () {
  346. $('#loginip').val(cameraInfo.ip || '');
  347. $('#port').val(cameraInfo.port || '');
  348. $('#username').val(cameraInfo.username || '');
  349. $('#password').val(cameraInfo.password || '');
  350. clickLogin(cameraInfo.ip);
  351. }, 200);
  352. }
  353. // 当前选中的校区
  354. var currentCampus = '临安校区';
  355. // 切换校区
  356. function switchCampus(campus) {
  357. currentCampus = campus;
  358. // 更新tab样式
  359. if (campus === '西溪校区') {
  360. $('#xqxq-tab').addClass('campus-tab-active');
  361. $('#laxq-tab').removeClass('campus-tab-active');
  362. } else {
  363. $('#laxq-tab').addClass('campus-tab-active');
  364. $('#xqxq-tab').removeClass('campus-tab-active');
  365. }
  366. // 重新加载摄像头列表
  367. loadCameraList(campus);
  368. }
  369. // 加载摄像头列表
  370. function loadCameraList(campus) {
  371. var requestBody = new FormData();
  372. var requestData = {
  373. condition: {
  374. xq: campus
  375. },
  376. requiredItems: "jxlmc,sxtmc,ipdz,dk,zh,mm,xq,pp"
  377. };
  378. requestBody.append('requestBody', JSON.stringify(requestData));
  379. $.ajax({
  380. url: 'http://172.16.0.63:28080/dmp-res-api/restservice/20241028113645823630',
  381. type: 'POST',
  382. contentType: false,
  383. data: requestBody,
  384. processData: false,
  385. success: function(response) {
  386. let a = response;
  387. console.log(a);
  388. let obj = [];
  389. for (let i = 0; i < a.resultData.length; i++) {
  390. //判断data中是否有值,没有就添加,有就跳过
  391. let flag = false;
  392. let index = 0;
  393. for (let j = 0; j < obj.length; j++) {
  394. if (obj[j].buildname == a.resultData[i].jxlmc) {
  395. flag = true;
  396. index = j;
  397. break;
  398. }
  399. }
  400. if (flag) {
  401. obj[index].classes.push({
  402. classname: a.resultData[i].sxtmc,
  403. ip: a.resultData[i].ipdz || '',
  404. port: a.resultData[i].dk || '',
  405. username: a.resultData[i].zh || '',
  406. password: a.resultData[i].mm || '',
  407. brand: a.resultData[i].pp || '',
  408. campus: a.resultData[i].xq || campus
  409. })
  410. }
  411. if (!flag) {
  412. obj.push({
  413. buildname: a.resultData[i].jxlmc,
  414. classes: [{
  415. classname: a.resultData[i].sxtmc,
  416. ip: a.resultData[i].ipdz || '',
  417. port: a.resultData[i].dk || '',
  418. username: a.resultData[i].zh || '',
  419. password: a.resultData[i].mm || '',
  420. brand: a.resultData[i].pp || '',
  421. campus: a.resultData[i].xq || campus
  422. }]
  423. })
  424. }
  425. }
  426. console.log(obj)
  427. app.$data.message = obj;
  428. },
  429. error: function(xhr, status, error) {
  430. console.error('Error:', error);
  431. }
  432. });
  433. }
  434. var app = new Vue({
  435. el: '#menu-container',
  436. data: {
  437. message:[],
  438. },
  439. mounted: function () {
  440. console.log("mounted");
  441. },
  442. created: function(){
  443. console.log("created");
  444. // 初始化校区tab样式
  445. $('#laxq-tab').addClass('campus-tab-active');
  446. $('#xqxq-tab').removeClass('campus-tab-active');
  447. // 加载默认校区(临安校区)的摄像头列表
  448. loadCameraList('临安校区');
  449. },
  450. methods: {
  451. handleClick(cameraInfo) {
  452. // cameraInfo包含:ip, port, username, password, brand等
  453. // 所有信息都从接口获取,不再使用写死的默认值
  454. if (!cameraInfo.ip) {
  455. console.error('摄像头IP地址为空,无法登录');
  456. alert('摄像头信息不完整,无法登录');
  457. return;
  458. }
  459. console.log('[CLICK] cameraInfo:', cameraInfo);
  460. if (isDahuaBrand(cameraInfo.brand)) {
  461. console.log('[ROUTE] DAHUA area');
  462. setTimeout(function () {
  463. playDahuaCamera(cameraInfo);
  464. }, 100);
  465. } else if (isHikvisionBrand(cameraInfo.brand)) {
  466. console.log('[ROUTE] HIKVISION area');
  467. setTimeout(function () {
  468. playHikvisionCamera(cameraInfo);
  469. }, 100);
  470. } else {
  471. console.log('[ROUTE] default to HIKVISION when brand unknown');
  472. setTimeout(function () {
  473. playHikvisionCamera(cameraInfo);
  474. }, 100);
  475. }
  476. }
  477. }
  478. })
  479. </script>
  480. <style>
  481. /* 设置整个页面为100%宽高 */
  482. html, body {
  483. width: 100%;
  484. height: 100%;
  485. margin: 0;
  486. padding: 0;
  487. overflow: hidden;
  488. }
  489. /* 覆盖外部CSS的固定尺寸,改为100%自适应 */
  490. .main {
  491. width: 100% !important;
  492. height: 100% !important;
  493. display: flex;
  494. flex-direction: column;
  495. background-size: cover;
  496. background-position: center;
  497. }
  498. .main-header {
  499. width: 100% !important;
  500. height: auto !important;
  501. min-height: 105px;
  502. flex-shrink: 0;
  503. background-size: cover;
  504. background-position: center;
  505. }
  506. .main-container {
  507. width: 100% !important;
  508. height: 100% !important;
  509. flex: 1;
  510. display: flex;
  511. overflow: hidden;
  512. min-height: 0;
  513. padding: 0 16px 16px 16px;
  514. box-sizing: border-box;
  515. }
  516. /* 左侧菜单容器 - 固定宽度,高度100% */
  517. .menu-container {
  518. width: 468px !important;
  519. height: 100% !important;
  520. flex-shrink: 0;
  521. border: 1px solid #056AE7;
  522. margin-right: 5px;
  523. overflow: auto;
  524. display: flex;
  525. flex-direction: column;
  526. box-sizing: border-box;
  527. }
  528. .campus-tabs {
  529. flex-shrink: 0;
  530. }
  531. .title-item {
  532. flex-shrink: 0;
  533. }
  534. /* 右侧视频容器 - 自适应剩余空间 */
  535. .video-container {
  536. width: 100% !important;
  537. height: 100% !important;
  538. flex: 1;
  539. padding: 0 8px 8px 8px;
  540. display: flex;
  541. flex-direction: column;
  542. gap: 12px;
  543. overflow: hidden;
  544. min-height: 0;
  545. border: 1px solid #056AE7;
  546. margin-left: 5px;
  547. box-sizing: border-box;
  548. }
  549. .video-title {
  550. display: flex;
  551. align-items: center;
  552. justify-content: space-between;
  553. flex-shrink: 0;
  554. }
  555. .hikvision-block, .dahua-block {
  556. background: rgba(0,0,0,0.15);
  557. padding: 8px;
  558. border: 1px solid rgba(255,255,255,0.1);
  559. flex: 1;
  560. display: flex;
  561. flex-direction: column;
  562. min-height: 0;
  563. overflow: hidden;
  564. }
  565. .hikvision-body {
  566. margin-top: 8px;
  567. flex: 1;
  568. display: flex;
  569. flex-direction: column;
  570. min-height: 0;
  571. }
  572. .hikvision-body .plugin {
  573. flex: 1;
  574. background: #000;
  575. min-height: 0;
  576. width: 100%;
  577. }
  578. /* 大华播放区 - 自适应高度 */
  579. #dahua-player-shell {
  580. flex: 1;
  581. min-height: 0;
  582. display: flex;
  583. flex-direction: column;
  584. }
  585. #dahua-player-shell .content {
  586. flex: 1;
  587. min-height: 0;
  588. display: flex;
  589. }
  590. #dahua-player-shell .content .h5-left:nth-child(2) {
  591. flex: 1;
  592. min-height: 0;
  593. display: flex;
  594. flex-direction: column;
  595. }
  596. #dahua-player-shell .h5-play-wrap {
  597. flex: 1;
  598. min-height: 0;
  599. width: 100% !important;
  600. height: 100% !important;
  601. }
  602. #dahua-player-shell .h5-play-wrap div[wnd-index] {
  603. height: 100%;
  604. width: 100%;
  605. }
  606. /* 确保class-container自适应 */
  607. .class-container {
  608. width: 100% !important;
  609. max-width: 100%;
  610. box-sizing: border-box;
  611. }
  612. /* 移除h5-left和h5-right的margin-right */
  613. .h5-left, .h5-right {
  614. margin-right: 0 !important;
  615. }
  616. /* 移除container的padding */
  617. .container {
  618. padding: 0 !important;
  619. }
  620. /* 导航栏适配2560*1440屏幕(从1920*1080按1.3333比例缩放) */
  621. .main-header {
  622. position: relative;
  623. }
  624. .sy, .jyjx, .zsxx, .jyxx, .zwhq, .jsxx, .xsxx, .shfw, .jxxc {
  625. width: 164px !important;
  626. height: 56px !important;
  627. top: 32px !important;
  628. background-size: cover !important;
  629. background-position: center center !important;
  630. }
  631. .sy {
  632. left: 5.3516vw !important; /* 137px / 2560px * 100 */
  633. }
  634. .jyjx {
  635. left: 12.4609vw !important; /* 319px / 2560px * 100 */
  636. }
  637. .zsxx {
  638. left: 19.5313vw !important; /* 500px / 2560px * 100 */
  639. }
  640. .jyxx {
  641. left: 26.6016vw !important; /* 681px / 2560px * 100 */
  642. }
  643. .zwhq {
  644. left: 63.3203vw !important; /* 1621px / 2560px * 100 */
  645. }
  646. .jsxx {
  647. left: 70.4297vw !important; /* 1803px / 2560px * 100 */
  648. }
  649. .xsxx {
  650. left: 77.5vw !important; /* 1984px / 2560px * 100 */
  651. }
  652. .shfw {
  653. left: 84.5703vw !important; /* 2165px / 2560px * 100 */
  654. }
  655. .jxxc {
  656. left: 91.6797vw !important; /* 2347px / 2560px * 100 */
  657. }
  658. .sy div, .jyjx div, .zsxx div, .jyxx div, .zwhq div, .jsxx div, .xsxx div, .shfw div, .jxxc div {
  659. width: 164px !important;
  660. height: 56px !important;
  661. line-height: 56px !important;
  662. font-size: 27px !important;
  663. }
  664. </style>
  665. </body>
  666. </html>