459 lines
23 KiB
HTML
459 lines
23 KiB
HTML
<div id="menu">
|
|
<menu :class="['menu',{'sidebar-hovered': isSidebarHovered}]">
|
|
<header class="top-header">
|
|
<nav class="navbar navbar-expand justify-content-between">
|
|
<div class="btn-toggle-menu" @click="onToggleMenu">
|
|
<i class="fa-solid fa-bars"></i>
|
|
</div>
|
|
<div class="position-relative search-bar d-lg-block d-none" @click="onShowSearchModal">
|
|
<input class="form-control form-control-sm rounded-5 px-5 lp-cursor-pointer" type="search" :placeholder="placeholderVal" readonly>
|
|
<span class="position-absolute ms-3 translate-middle-y start-0 top-50">
|
|
<i class="fa fa-search"></i>
|
|
</span>
|
|
</div>
|
|
<ul class="navbar-nav top-right-menu gap-2">
|
|
<li class="nav-item">
|
|
<a class="nav-link" style="font-size: 20px;">
|
|
<cn @click="updateSysLanguage('en')">En</cn>
|
|
<en @click="updateSysLanguage('cn')">中</en>
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" v-if="Object.keys(themeConf).length > 0">
|
|
<i v-if="themeConf.used==='default'" class="fa-regular fa-moon" @click="updateThemeConf('dark')"></i>
|
|
<i v-else class="fa-regular fa-sun" @click="updateThemeConf('default')"></i>
|
|
</a>
|
|
</li>
|
|
<li class="nav-item" v-if="Object.keys(hardwareConf).length > 0 && (hardwareConf.fac !== 'REC1' || hardwareConf.fac !== 'ENC8')">
|
|
<a class="nav-link" style="font-size: 19px;" @click="changeWeb">
|
|
<i class="fa-solid fa-arrow-right-arrow-left"></i>
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<usb-option></usb-option>
|
|
</li>
|
|
<!-- <li class="nav-item">-->
|
|
<!-- <a class="nav-link" data-bs-toggle="offcanvas" href="#ThemeCustomizer"><i class="fa fa-gear"></i></a>-->
|
|
<!-- </li>-->
|
|
</ul>
|
|
</nav>
|
|
|
|
</header>
|
|
|
|
<aside class="sidebar-wrapper" @mouseenter="onMouseEnter" @mouseleave="onMouseLeave">
|
|
<div class="sidebar-header">
|
|
<div class="logo-icon lp-cursor-pointer" @click="onHrefDash">
|
|
<img src="assets/img/logo.png" width="78">
|
|
</div>
|
|
<div class="logo-name flex-grow-1">
|
|
<!-- <h5 class="mb-0 fw-bold">LinkPi</h5>-->
|
|
</div>
|
|
|
|
<div class="sidebar-close" @click="onCloseSidebar">
|
|
<i class="fa-solid fa-xmark"></i>
|
|
</div>
|
|
</div>
|
|
<div class="sidebar-nav" data-simplebar>
|
|
<ul class="metismenu" ref="metismenu">
|
|
<li>
|
|
<a href="dashboard.php">
|
|
<div class="parent-icon"><i class="fa fa-tachometer"></i></div>
|
|
<div class="menu-title"><cn>设备状态</cn><en>Dashboard</en></div>
|
|
</a>
|
|
</li>
|
|
<li class="menu-label"><cn>基本设置</cn><en>Basic</en></li>
|
|
<li>
|
|
<a href="decode.php">
|
|
<div class="parent-icon"><i class="fa fa-image"></i></div>
|
|
<div class="menu-title"><cn>解码设置</cn><en>Decode</en></div>
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a href="encode.php">
|
|
<div class="parent-icon"><i class="fa-brands fa-joomla"></i></div>
|
|
<div class="menu-title"><cn>编码设置</cn><en>Encode</en></div>
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a href="stream.php">
|
|
<div class="parent-icon"><i class="fa fa-upload"></i></div>
|
|
<div class="menu-title"><cn>串流输出</cn><en>Stream</en></div>
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a href="push.php">
|
|
<div class="parent-icon"><i class="fa-solid fa-fan"></i></div>
|
|
<div class="menu-title"><cn>直播推流</cn><en>Push</en></div>
|
|
</a>
|
|
</li>
|
|
<li v-if="Object.keys(hardwareConf).length > 0 && hardwareConf.function.videoOut">
|
|
<a href="output.php">
|
|
<div class="parent-icon"><i class="fa-solid fa-microchip"></i></div>
|
|
<div class="menu-title"><cn>接口输出</cn><en>Output</en></div>
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a href="sys.php">
|
|
<div class="parent-icon"><i class="fa-solid fa-gear"></i></div>
|
|
<div class="menu-title"><cn>系统设置</cn><en>System</en></div>
|
|
</a>
|
|
</li>
|
|
<li v-if="Object.keys(hardwareConf).length > 0 && (hardwareConf.function.overlay || hardwareConf.function.mix)" class="menu-label"><cn>视频特效</cn><en>Effect</en></li>
|
|
<li v-if="Object.keys(hardwareConf).length > 0 && hardwareConf.function.overlay">
|
|
<a href="overlay.php">
|
|
<div class="parent-icon"><i class="fa-solid fa-wand-magic-sparkles"></i></div>
|
|
<div class="menu-title"><cn>叠加特效</cn><en>Overlay</en></div>
|
|
</a>
|
|
</li>
|
|
<li v-if="Object.keys(hardwareConf).length > 0 && hardwareConf.function.mix">
|
|
<a href="mix.php">
|
|
<div class="parent-icon"><i class="fa-solid fa-chart-simple"></i></div>
|
|
<div class="menu-title"><cn>视频混合</cn><en>Video mix</en></div>
|
|
</a>
|
|
</li>
|
|
<li class="menu-label"><cn>其他设置</cn><en>OTHER</en></li>
|
|
<li>
|
|
<a href="javascript:;" class="has-arrow">
|
|
<div class="parent-icon"><i class="fa-solid fa-puzzle-piece ms-1 mb-1"></i></div>
|
|
<div class="menu-title"><cn>扩展功能</cn><en>Extend</en></div>
|
|
</a>
|
|
<ul class="ps-3">
|
|
<li v-if="Object.keys(hardwareConf).length > 0 && hardwareConf.function.ndi">
|
|
<a href="ndi.php">
|
|
<i class="fa-solid fa-desktop"></i>
|
|
<div class="menu-title"><cn>NDI解码</cn><en>NDI Decode</en></div>
|
|
</a>
|
|
</li>
|
|
<li v-if="Object.keys(hardwareConf).length > 0 && hardwareConf.function.record">
|
|
<a href="record.php">
|
|
<i class="fa-solid fa-folder-open"></i>
|
|
<div class="menu-title"><cn>文件录制</cn><en>Record</en></div>
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a href="player.php">
|
|
<i class="fa-solid fa-circle-play"></i>
|
|
<div class="menu-title"><cn>H5 播放器</cn><en>H5 Player</en></div>
|
|
</a>
|
|
</li>
|
|
<li v-if="Object.keys(hardwareConf).length > 0 && hardwareConf.function.intercom">
|
|
<a href="intercom.php">
|
|
<i class="fa-solid fa-headset"></i>
|
|
<div class="menu-title"><cn>集成通信</cn><en>Intercom</en></div>
|
|
</a>
|
|
</li>
|
|
<li v-if="Object.keys(hardwareConf).length > 0 && (hardwareConf.function.serialport || hardwareConf.function.button)">
|
|
<a href="uart.php">
|
|
<i class="fa-solid fa-link"></i>
|
|
<div class="menu-title"><cn>串口、按键</cn><en>Serial, Button</en></div>
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
</li>
|
|
<li>
|
|
<a href="javascript:;" class="has-arrow">
|
|
<div class="parent-icon"><i class="fa-solid fa-gears"></i></div>
|
|
<div class="menu-title"><cn>高级设置</cn><en>Options</en></div>
|
|
</a>
|
|
<ul class="ps-3">
|
|
<li>
|
|
<a href="group.php">
|
|
<i class="fa-solid fa-server"></i>
|
|
<div class="menu-title"><cn>群组设置</cn><en>Group</en></div>
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a href="rproxy.php">
|
|
<i class="fa-solid fa-comments"></i>
|
|
<div class="menu-title"><cn>远程访问</cn><en>Reverse Proxy</en></div>
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a href="service.php">
|
|
<i class="fa-solid fa-cloud"></i>
|
|
<div class="menu-title"><cn>服务设置</cn><en>Service</en></div>
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
</li>
|
|
<li>
|
|
<a href="javascript:;" class="has-arrow">
|
|
<div class="parent-icon"><i class="fa-solid fa-flask"></i></div>
|
|
<div class="menu-title"><cn>实验功能</cn><en>Laboratory</en></div>
|
|
</a>
|
|
<ul class="ps-3">
|
|
<li>
|
|
<a href="gb28181.php">
|
|
<i class="fa-solid fa-cloud"></i>
|
|
<div class="menu-title"><cn>GB28181</cn><en>GB28181</en></div>
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a href="roi.php">
|
|
<i class="fa-solid fa-circle-user"></i>
|
|
<div class="menu-title"><cn>ROI</cn><en>ROI</en></div>
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a href="insta360.php">
|
|
<i class="fa-solid fa-camera"></i>
|
|
<div class="menu-title"><cn>Insta360 Link</cn><en>Insta360 Link</en></div>
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a href="onvif.php">
|
|
<i class="fa fa-video-camera"></i>
|
|
<div class="menu-title"><cn>Onvif PTZ</cn><en>Onvif PTZ</en></div>
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a href="sync.php">
|
|
<i class="fa-solid fa-bars-staggered"></i>
|
|
<div class="menu-title"><cn>同步调节</cn><en>Synchronization</en></div>
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a href="colorKey.php">
|
|
<i class="fa fa-cut"></i>
|
|
<div class="menu-title"><cn>抠像</cn><en>ColorKey</en></div>
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
<div class="sidebar-bottom">
|
|
<div class="d-flex align-items-center px-3 gap-3 w-100 h-100" data-bs-toggle="dropdown">
|
|
<div class="user-icon">
|
|
<i class="fa-regular fa-user"></i>
|
|
</div>
|
|
<div class="user-info">
|
|
<h5 class="mb-0 user-name">Admin</h5>
|
|
<p class="mb-0 user-designation"><cn>系统管理员</cn><en>administrator</en></p>
|
|
</div>
|
|
<div class="logout-icon" @click="onLogout">
|
|
<i class="fa-solid fa-right-from-bracket"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</aside>
|
|
<div class="modal fade" tabindex="-1" aria-hidden="true" ref="modal">
|
|
<div class="modal-dialog modal-dialog-centered modal-dialog-scrollable modal-lg" style="z-index: 99999">
|
|
<div class="modal-content">
|
|
<div class="modal-header gap-2">
|
|
<div class="position-relative popup-search w-100">
|
|
<input class="form-control form-control-lg ps-5 border border-2 search-input" type="text" v-model.trim="searchVal">
|
|
<span class="position-absolute ms-3 translate-middle-y start-0 top-50">
|
|
<i class="fa fa-search search-bar-icon"></i>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
<div class="modal-body" style="height: 350px">
|
|
<div data-simplebar>
|
|
<div v-if="searchRes.length > 0" class="search-list">
|
|
<div class="mb-4" v-for="(item,index) in searchRes" :key="index">
|
|
<div class="d-flex">
|
|
<div class="search-item-icon"><i :class="['font-14',item.icon]"></i></div>
|
|
<span class="mb-2 ms-2 font-16">{{item.name}}</span>
|
|
</div>
|
|
<div class="list-group">
|
|
<a v-for="(itm,idx) in item.filter" :key="idx" @click="onRedirect(item.url,itm)" class="list-group-item list-group-item-action align-items-center d-flex gap-2 lp-cursor-pointer search-item-option">{{itm}}</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div v-else class="h-100 lp-align-center">
|
|
<div>
|
|
<div class="row">
|
|
<empty-box></empty-box>
|
|
</div>
|
|
<div class="row mt-3">
|
|
<div class="text-center search-tip-text">
|
|
<cn>请输入要检索的标题关键字</cn>
|
|
<en>Please enter the keyword you want to search</en>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="modal-backdrop show" @click="onHideSearchModal"></div>
|
|
</div>
|
|
</menu>
|
|
</div>
|
|
<script src="../assets/plugins/passive/passive.events.min.js"></script>
|
|
<script type="module">
|
|
import vue from "../assets/js/vue.build.js";
|
|
import Metismenu from '../assets/plugins/metismenu/js/metisMenu.esm.js';
|
|
import mutationObserver from "../assets/plugins/polyfill/mutationobserver.esm.js";
|
|
import { func,updateSysLanguage,confirm,isEmpty,clearReactiveArray } from "../assets/js/lp.utils.js";
|
|
import { useThemeConf,useHardwareConf } from "../assets/js/vue.hooks.js"
|
|
import { ignoreCustomElementPlugin,usbOptionComponent } from "../assets/js/vue.helper.js"
|
|
import { emptyBoxFlagComponent } from "../assets/js/vue.flags.js"
|
|
import { md5 } from "../assets/plugins/md5/js.md5.esm.js";
|
|
|
|
|
|
const { createApp,ref,reactive,watchEffect,watch,nextTick,onMounted } = vue;
|
|
const menu = createApp({
|
|
components:{
|
|
"usb-option": usbOptionComponent,
|
|
"empty-box": emptyBoxFlagComponent
|
|
},
|
|
setup() {
|
|
const { themeConf,updateThemeConf } = useThemeConf();
|
|
const { hardwareConf } = useHardwareConf();
|
|
|
|
const state = {
|
|
metismenu: ref(null),
|
|
isMenuToggled : ref(false),
|
|
isSidebarHovered: ref(false),
|
|
showFormatModal: ref(false),
|
|
modal: ref(null),
|
|
bsModal: ref(null),
|
|
searchVal: ref(""),
|
|
searchRes: reactive([]),
|
|
placeholderVal: ref("关键字")
|
|
}
|
|
|
|
const onLogout = () => {
|
|
func("/login/onLogout")
|
|
.then((ret)=>{
|
|
if(ret.status === "success")
|
|
location.href = "/";
|
|
});
|
|
}
|
|
|
|
const unwatch = watch(hardwareConf,()=>{
|
|
if(Object.keys(hardwareConf).length > 0) {
|
|
nextTick(()=>{
|
|
let as = document.querySelectorAll(".sidebar-wrapper .metismenu li a");
|
|
for (let i = 0; i < as.length; i++) {
|
|
if (as[i].href === window.location.href) {
|
|
as[i].classList.add("active");
|
|
let parent = as[i].parentElement;
|
|
while (parent && parent.tagName === "LI") {
|
|
parent.classList.add("mm-active");
|
|
parent = parent.parentElement;
|
|
if (parent && parent.tagName === "UL")
|
|
parent.classList.add("mm-show");
|
|
parent = parent.parentElement;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
unwatch();
|
|
})
|
|
}
|
|
})
|
|
|
|
watch(state.searchVal, () => {
|
|
if (isEmpty(state.searchVal.value)) {
|
|
clearReactiveArray(state.searchRes);
|
|
return;
|
|
}
|
|
const html = document.querySelector("html");
|
|
const lang = html.getAttribute('data-bs-language');
|
|
const param = {"lang": lang, "filter": state.searchVal.value};
|
|
func("/root/filterKeywords", param).then(result => {
|
|
clearReactiveArray(state.searchRes);
|
|
state.searchRes.push(...result.data);
|
|
});
|
|
});
|
|
|
|
watch(state.isMenuToggled,() => {
|
|
const body = document.querySelector('body');
|
|
if(state.isMenuToggled.value)
|
|
body.classList.add('toggled');
|
|
else
|
|
body.classList.remove('toggled');
|
|
})
|
|
|
|
const onHrefDash = () => {
|
|
location.href="/dashboard.php";
|
|
}
|
|
|
|
const onToggleMenu = () => {
|
|
state.isMenuToggled.value = !state.isMenuToggled.value;
|
|
}
|
|
|
|
const onMouseEnter = () => {
|
|
if(state.isMenuToggled.value)
|
|
state.isSidebarHovered.value = true;
|
|
};
|
|
|
|
const onMouseLeave = () => {
|
|
if(state.isSidebarHovered.value)
|
|
state.isSidebarHovered.value = false;
|
|
};
|
|
|
|
const onCloseSidebar = () => {
|
|
state.isMenuToggled.value = false;
|
|
}
|
|
|
|
const changeWeb = () => {
|
|
confirm({
|
|
title: '<cn>切换版本</cn><en>Switch Web</en>',
|
|
content: '<cn>是否切换至经典版页面,并重新启动?</cn><en>Whether to switch to the classic version and restart?</en>',
|
|
buttons: {
|
|
ok: {
|
|
text: "<cn>切换</cn><en>Switch</en>",
|
|
btnClass: 'btn-primary',
|
|
keys: ['enter'],
|
|
action: () => func("/system/changeWebVersion","classic")
|
|
},
|
|
cancel: {
|
|
text: "<cn>取消</cn><en>Cancel</en>"
|
|
}
|
|
|
|
}
|
|
});
|
|
}
|
|
|
|
const onShowSearchModal = () => {
|
|
state.bsModal.value.show();
|
|
const ele = document.querySelector("body > .modal-backdrop");
|
|
ele.remove();
|
|
}
|
|
|
|
const onHideSearchModal = () => {
|
|
state.bsModal.value.hide();
|
|
}
|
|
|
|
const onRedirect = (url,data) => {
|
|
location.href = url+"?filter="+md5(data);
|
|
}
|
|
|
|
onMounted(()=>{
|
|
new Metismenu(state.metismenu.value);
|
|
state.bsModal.value = new bootstrap.Modal(state.modal.value);
|
|
|
|
const update = () => {
|
|
const lang = html.getAttribute('data-bs-language');
|
|
if(lang === "cn")
|
|
state.placeholderVal.value = "关键字";
|
|
else
|
|
state.placeholderVal.value = "Search";
|
|
}
|
|
|
|
const html = document.querySelector('html');
|
|
update();
|
|
const observer = new mutationObserver(() => {
|
|
update();
|
|
});
|
|
const config = {
|
|
attributes: true,
|
|
attributeFilter: ["data-bs-language"],
|
|
subtree: false
|
|
};
|
|
observer.observe(html, config);
|
|
});
|
|
|
|
return {...state,hardwareConf,themeConf,updateThemeConf,onHrefDash,onLogout,onToggleMenu,
|
|
onMouseEnter,onMouseLeave,onCloseSidebar,updateSysLanguage,changeWeb,onShowSearchModal,
|
|
onHideSearchModal,onRedirect}
|
|
}
|
|
});
|
|
menu.use(ignoreCustomElementPlugin);
|
|
menu.mount('#menu');
|
|
</script> |