import vue from "./vue.build.js"; import $ from '../plugins/jquery/jquery.esm.js'; import '../plugins/switch/js/bootstrap-switch.min.js'; import "../plugins/timepicker/js/bootstrap-timepicker.js"; import * as noUiSlider from "../plugins/nouislider/js/nouislider.esm.js"; import mutationObserver from '../plugins/polyfill/mutationobserver.esm.js' import { md5 } from "../plugins/md5/js.md5.esm.js"; import { func, confirm, rebootConfirm, alertMsg, axios_post, isEmpty, formatTime, clearReactiveArray, getUrlParam } from './lp.utils.js' import { useDiskConf } from "./vue.hooks.js"; const { ref, reactive, toRefs, watch, watchEffect, computed, onMounted, nextTick, defineAsyncComponent } = vue; export const ignoreCustomElementPlugin = { install: (app) => { app.config.compilerOptions.isCustomElement = (tag) => tag === 'cn' || tag === 'en'; } }; export const filterKeywordPlugin = { install(app) { const filter = getUrlParam("filter"); if(!filter) return; const param = {"url":location.pathname, "filter":filter} func("/root/getFilterKeywords",param).then(result => { const keyword = result.data; if (keyword) { // nextTick(() => { setTimeout(()=>{ const elements = document.querySelectorAll('main cn, main en'); elements.forEach((el) => { const textContent = el.textContent; const startIndex = textContent.indexOf(keyword); if (startIndex !== -1) { let currentElement = el.parentNode; while (currentElement) { currentElement = currentElement.parentNode; if(currentElement) { let classList = currentElement.classList; if(classList && classList.contains("tab-pane")) { let tabId = currentElement.id; let navLinks = document.querySelectorAll(".nav.nav-tabs > .nav-item > .nav-link"); navLinks.forEach(item => { if (item.getAttribute('href') === '#'+tabId) { if(item.classList) item.classList.add("active"); } else { if(item.classList) item.classList.remove("active"); } }) const siblings = Array.from(currentElement.parentElement.children); siblings.forEach((sibling) => { sibling.classList.remove('active'); sibling.classList.remove('show'); }); currentElement.classList.add('active'); currentElement.classList.add('show'); } } } const endIndex = startIndex + keyword.length; const beforeText = textContent.slice(0, startIndex); const highlightedText = textContent.slice(startIndex, endIndex); const afterText = textContent.slice(endIndex); const highlightedElement = document.createElement('span'); highlightedElement.style.fontWeight = '700'; highlightedElement.style.color = 'red'; highlightedElement.textContent = highlightedText; el.innerHTML = beforeText; el.appendChild(highlightedElement); el.innerHTML += afterText; } }) },100); // }) } }) } }; export const languageOptionDirective = { mounted(el, binding, vnode) { const update = () => { const lang = html.getAttribute('data-bs-language'); el.textContent = el.getAttribute(lang); } 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); } }; export const clickOutsideDirective = { mounted(el, binding) { const eventHandler = (e) => { if (el.contains(e.target)) { return false } if (binding.value && typeof binding.value === 'function') { binding.value(e) } } el.__click_outside__ = eventHandler document.addEventListener('click', eventHandler) }, beforeUnmount(el) { document.removeEventListener('click', el.__click_outside__) delete el.__click_outside__ } } export const statusTemperatureComponent = { template: `
0℃
`, props: { modelValue: { type: Number, default: 0 }, activeColor: { type: String, default: "#fb0" } }, setup(props, context) { const tmp_mask = ref(null); const tmp_text = ref(null); const {modelValue, activeColor} = toRefs(props); watch(modelValue, () => { tmp_mask.value.style.bottom = modelValue.value + '%'; tmp_text.value.textContent = modelValue.value + '℃'; }) onMounted(() => tmp_mask.value.parentElement.style.background = activeColor.value); return {tmp_mask, tmp_text} } }; export const statusPieChartComponent = { template: `
`, props: { modelValue: { type: Number, default: 0 }, activeColor: { type: String, default: "#fb0" }, trackColor: { type: String, default: "#777" } }, setup(props, context) { const pie_chart = ref(null); const pie_text = ref(null); const {modelValue} = toRefs(props); watch(modelValue, newValue => { if ($(pie_chart.value).data('easyPieChart')) $(pie_chart.value).data('easyPieChart').update(newValue); }) onMounted(() => { pie_text.value.textContent = "0%"; $(pie_chart.value).easyPieChart({ easing: 'easeOutElastic', delay: 2000, barColor: props.activeColor, trackColor: props.trackColor, scaleColor: false, lineWidth: 20, trackWidth: 16, lineCap: 'butt', width: 50, onStep: (from, to, percent) => { pie_text.value.textContent = Math.round(percent) + "%"; } }); }) return {pie_chart, pie_text} } }; export const netFlotChartComponent = { template: `
`, props: { line1Color: { type: String, default: "#FB0" }, line2Color: { type: String, default: "#555" }, maxy: { type: Number, maxy: 800 }, data1: { type: Array, default: [], }, data2: { type: Array, default: [] }, tickColor: { type: String, default: "#eee" }, borderColor: { type: String, default: "#ccc" }, tipBorderColor: { type: String, default: "#fb0" }, tipBgColor: { type: String, default: "#fff" }, tipTxtColor: { type: String, default: "#555" } }, setup(props, context) { const net_chart = ref(null); let plot = {}; const showTooltip = (x, y, color, contents) => { $('
' + contents + '
').css({ position: 'absolute', display: 'none', top: y - 40, left: x - 120, border: '2px solid ' + props.tipBorderColor, padding: '3px', 'font-size': '9px', 'border-radius': '5px', 'color': props.tipTxtColor, 'background-color': props.tipBgColor, 'font-family': 'Verdana, Arial, Helvetica, Tahoma, sans-serif', opacity: 0.9 }).appendTo("body").fadeIn(200); } const initPlot = () => { if (Object.keys(plot).length === 0) { let color = props.color; let data1 = props.data1; let data2 = props.data2; plot = $.plot(net_chart.value, [ { data: data1, lines: { fill: true, } }, { data: data2, lines: { show: true, } }] , { series: { lines: { show: true, fill: true, }, shadowSize: 0 }, yaxis: { min: 0, max: 800, tickSize: 160, tickFormatter: (v, axis) => { if (axis.max < 1024) return v + "Kb/s"; else { v /= 1024; if (axis.max < 10240) return v.toFixed(2) + "Mb/s"; else return Math.floor(v) + "Mb/s"; } } }, xaxis: { show: false }, grid: { hoverable: true, clickable: true, tickColor: props.tickColor, borderWidth: 1, borderColor: props.borderColor, }, colors: [props.line1Color, props.line2Color], tooltip: false }); $.fn.tooltip = () => { let prePoint = null, preLabel = null; $(net_chart.value).bind("plothover", (event, pos, item) => { if (item) { if ((preLabel !== item.series.label) || (prePoint !== item.dataIndex)) { prePoint = item.dataIndex; preLabel = item.series.label; $("#tooltip").remove(); $(this).css({ "cursor": "pointer" }) let data = item.series.data[item.dataIndex][1]; if (data > 1024) data = parseInt(data / 1024) + "Mb/s"; else data += "kb/s"; if (item.seriesIndex === 0) showTooltip(item.pageX + 100, item.pageY - 10, color, "上行upward: " + data); if (item.seriesIndex === 1) showTooltip(item.pageX + 100, item.pageY - 10, color, "下行downward: " + data); } } else { prePoint = null; preLabel = null; $(this).css({ "cursor": "auto" }); $("#tooltip").remove(); } }); } $(net_chart.value).tooltip(); } } const updatePlot = () => { if (Object.keys(plot).length !== 0) { let maxy = props.maxy; let data1 = props.data1; let data2 = props.data2; plot.setData([data1, data2]); plot.draw(); plot.getOptions().yaxes[0].max = maxy; plot.getOptions().yaxes[0].tickSize = Math.floor(maxy / 5); plot.setupGrid(); } } watch(props.data1, () => { updatePlot(); }, {deep: true}) onMounted(() => { setTimeout(initPlot, 100); }) return {net_chart} } } export const bootstrapSwitchComponent = { template: ``, props: { modelValue: { type: Boolean, default: false }, size: { type: String, default: "small" //normal } }, setup(props, context) { const {modelValue, size} = toRefs(props); const bs_switch = ref(null); watch(modelValue, () => { $(bs_switch.value).bootstrapSwitch('state', modelValue.value, true); }) onMounted(() => { $(bs_switch.value).bootstrapSwitch({ "state": props.modelValue, "size": size.value, onInit(dom, event, state) { $(bs_switch.value).on('focus.bootstrapSwitch', () => { this.$wrapper.removeClass("bootstrap-switch-focused") }) }, onSwitchChange(event, state) { context.emit('update:modelValue', state); context.emit('switch-change', state); } }) }) return {bs_switch} } }; export const multipleSelectComponent = { template: ``, props: { value1: { type: [Number, String, Boolean], default: 0 }, value2: { type: [Number, String, Boolean], default: 0 }, split: { type: String, default: 0 } }, setup(props, context) { let selectValue = ref(""); watchEffect(() => { selectValue.value = props.value1 + props.split + props.value2; }) const parseValue = (value) => { if (value === "true" || value === "false") { return JSON.parse(value); } return isNaN(Number(value)) ? value : Number(value); }; const onSelectChange = () => { let [value1, value2] = selectValue.value.split(props.split); context.emit('update:value1', parseValue(value1)); context.emit('update:value2', parseValue(value2)); } onMounted(() => { selectValue.value = props.value1 + props.split + props.value2; }) return {selectValue, onSelectChange} } }; export const multipleInputComponent = { template: ``, props: { value1: { type: [Number, String], default: 0 }, value2: { type: [Number, String], default: 0 }, split: { type: String, default: 0 } }, setup(props, context) { let selectValue = ref(""); const {value1, value2} = toRefs(props); watchEffect(() => { const val1 = (typeof value1.value === "string") ? value1.value.trim() : value1.value; const val2 = (typeof value2.value === "string") ? value2.value.trim() : value2.value; selectValue.value = val1 + props.split + val2; }) const onInputChange = () => { let [val1, val2] = selectValue.value.split(props.split); val1 = isNaN(Number(val1)) ? val1 : Number(val1); val2 = isNaN(Number(val2)) ? val2 : Number(val2); if (typeof val1 === "string") val1 = val1.trim(); if (typeof val2 === "string") val2 = val2.trim(); context.emit('update:value1', val1); context.emit('update:value2', val2); selectValue.value = val1 + props.split + val2; } return {selectValue, onInputChange} } }; export const nouiSliderComponent = { template: `
`, props: { modelValue: { type: [Number, String], default: 0 }, min: { type: Number, default: 0 }, max: { type: Number, default: 100 }, step: { type: Number, default: 1 }, fix: { type: Number, default: 0 }, funcValue: { type: Number, }, format: { type: String, default: "" }, disable: { type: Boolean, default: false }, index: { type: Number, default: 0 } }, setup(props, context) { const slider = ref(null); let handle = ref(null); let hover = false; let isSlide = false; const showTooltip = () => { let tooltip = handle.value.querySelector(".noUi-tooltip") tooltip.style.display = 'block'; hover = true; } const hideTooltip = () => { let tooltip = handle.value.querySelector(".noUi-tooltip") tooltip.style.display = 'none'; hover = false; } const formatTooltipValue = value => { if (isEmpty(props.format)) { if (props.fix === 0) value = parseInt(value); else value = parseFloat(value).toFixed(props.fix); } if (props.format === "time") value = formatTime(value); return value; } watch(() => props.modelValue, (newValue, oldValue) => { if (slider.value && !isSlide) { slider.value.noUiSlider.updateOptions({ start: newValue, range: {'min': props.min, 'max': props.max}, }); if (!hover) hideTooltip(); } }); watch(() => props.funcValue, (newValue, oldValue) => { if (slider.value && !isSlide) { slider.value.noUiSlider.updateOptions({ start: newValue, range: {'min': props.min, 'max': props.max}, }); if (!hover) hideTooltip(); } }); watch(() => props.max, (newValue, oldValue) => { if (slider.value && !isSlide) { slider.value.noUiSlider.updateOptions({ range: {'min': props.min, 'max': newValue}, }); if (!hover) hideTooltip(); } }); onMounted(async () => { noUiSlider.create(slider.value, { start: props.modelValue, connect: [true, false], tooltips: { to: formatTooltipValue, }, range: {'min': props.min, 'max': props.max}, step: props.step, }); if (props.disable) slider.value.noUiSlider.disable(); handle.value = slider.value.querySelector('.noUi-handle'); hideTooltip(); slider.value.addEventListener('mouseenter', () => { showTooltip(); }); slider.value.addEventListener('mouseleave', () => { hideTooltip(); }); slider.value.noUiSlider.on('slide', (values, mark) => { isSlide = true; }); // slider.value.noUiSlider.on('end', (values, mark) => { // isSlide = false; // context.emit('update:modelValue', formatTooltipValue(values[mark])); // context.emit('slide-end', formatTooltipValue(values[mark]),props.index); // }); slider.value.noUiSlider.on('change', (values, mark) => { isSlide = false; context.emit('update:modelValue', formatTooltipValue(values[mark])); context.emit('slide-end', formatTooltipValue(values[mark]), props.index); }); }) return {slider} } }; export const h5PlayerComponent = { template: `
`, props: { url: { type: String, default: "" }, codec: { type: String, default: "h264" }, audio: { type: Boolean, default: true }, buffer: { type: Number, default: 200 }, canplay: { type: Boolean, default: true } }, setup(props, context) { const {url, codec, audio, buffer, canplay} = toRefs(props); const state = { videoHandler: ref(null), jessHandler: ref(null), cloudHandler: ref(null), hadInitPlayer: false, flvJsModule: {}, h5Player: {}, } watchEffect(() => { if (canplay.value) { if (url.value !== "") { if (state.hadInitPlayer) destroyPlayer(); setTimeout(initPlayer, 300); } } else { if (state.hadInitPlayer) destroyPlayer(); } }) const initPlayer = async () => { if (props.url === "") return; if (props.codec === "h265") { state.videoHandler.value.style.display = 'none'; state.jessHandler.value.style.display = 'block'; if (!window.Jessibuca) await import('../plugins/jessibuca/jessibuca.js'); state.h5Player = new Jessibuca({ container: state.jessHandler.value, videoBuffer: buffer.value / 1000, decoder: "assets/plugins/jessibuca/decoder.js", isResize: false, audio: JSON.parse(audio.value), operateBtns: { fullscreen: true, play: true, audio: JSON.parse(audio.value), }, forceNoOffscreen: true, isNotMute: false, }); state.h5Player.play(url.value); state.h5Player.on("play", (flag) => { state.cloudHandler.value.style.display = 'none' }) } else { state.videoHandler.value.style.display = 'block'; state.jessHandler.value.style.display = 'none'; if (!window.flvjs) await import('../plugins/flvjs/flv.js'); state.h5Player = flvjs.createPlayer({ type: 'flv', url: url.value, audio: JSON.parse(audio.value), }); state.h5Player.attachMediaElement(state.videoHandler.value); state.h5Player.load(); state.h5Player.play(); state.videoHandler.value.addEventListener("canplay", () => { state.cloudHandler.value.style.display = 'none' }); } state.hadInitPlayer = true; } const destroyPlayer = () => { if (Object.keys(state.h5Player).length > 0) { if (state.h5Player.hasOwnProperty("unload")) { state.h5Player.unload(); state.h5Player.detachMediaElement(); } state.h5Player.destroy(); state.h5Player = {}; } state.cloudHandler.value.style.display = 'flex'; state.videoHandler.value.removeEventListener("canplay", () => { }); state.hadInitPlayer = false; } const checkDelay = () => { // if (Object.keys(state.h5Player).length > 0 && state.h5Player.hasOwnProperty("buffered") && state.h5Player.buffered.length > 0) { // if (state.h5Player.buffered.end(0) - state.h5Player.currentTime > 1.5) { // state.h5Player.currentTime = state.h5Player.buffered.end(0) - 0.2; // } // } // setTimeout(checkDelay,1000); } onMounted(checkDelay); return {...state} } }; export const videoPlayerComponent = { template: `
`, props: { url: { type: String, default: "" }, canplay: { type: Boolean, default: true } }, setup(props, context) { const {url, canplay} = toRefs(props); const state = { videoHandler: ref(null), cloudHandler: ref(null), hadInitPlayer: false, } watchEffect(() => { if (canplay.value) { if (url.value !== "") { if (state.hadInitPlayer) destroyPlayer(); setTimeout(initPlayer, 300); } } else { if (state.hadInitPlayer) destroyPlayer(); } }) const initPlayer = () => { if (url.value === "") return; state.videoHandler.value.style.display = 'block'; state.videoHandler.value.src = url.value; state.videoHandler.value.play(); state.videoHandler.value.addEventListener("canplay", () => { state.cloudHandler.value.style.display = 'none' }); state.hadInitPlayer = true; } const destroyPlayer = () => { state.cloudHandler.value.style.display = 'flex'; state.videoHandler.value.removeEventListener("canplay", () => { }); state.hadInitPlayer = false; } return {...state} } }; export const timepickerComponent = { template: `
`, props: { modelValue: { type: String, default: "00:00" } }, setup(props, context) { const timepicker = ref(null); const {modelValue} = toRefs(props); watch(modelValue, () => { $(timepicker.value).timepicker('setTime', modelValue.value); }) onMounted(() => { $(timepicker.value).timepicker({ minuteStep: 1, defaultTime: props.modelValue, showMeridian: false, icons: { up: 'fa-solid fa-angle-up', down: 'fa-solid fa-angle-down' }, }); $(timepicker.value).on("changeTime.timepicker", event => { context.emit('update:modelValue', event.time.value); }); }) return {timepicker} } }; export const vueColorPickerComponent = { template: `
`, props: { modelValue: { type: String, default: "" }, direct: { type: String, default: "bottom" } }, components: { "sketch-picker": defineAsyncComponent(() => { return import('../plugins/vueColor/vue3.color.esm.js').then(module => { const {Sketch} = module; return Sketch; }) }), }, directives: { "click-outside": clickOutsideDirective }, setup(props, context) { const state = { picker: ref(null), popper: ref(null), pickerColor: ref(""), sketchColor: ref(""), partyPopper: {}, popperOptions: reactive({}), } watch(state.sketchColor, () => { if (state.sketchColor.value.hasOwnProperty("hex")) { state.pickerColor.value = state.sketchColor.value.hex; context.emit('update:modelValue', state.pickerColor.value); } }); const pickerColorChange = () => { state.sketchColor.value = state.pickerColor.value; context.emit('update:modelValue', state.pickerColor.value); } const showPopper = () => { state.popper.value.setAttribute('data-show', ''); state.partyPopper.setOptions((options) => ({ ...options, modifiers: [ ...options.modifiers, {name: 'eventListeners', enabled: true}, ], })); state.partyPopper.update(); } const hidePopper = () => { if (Object.keys(state.partyPopper).length > 0) { state.popper.value.removeAttribute('data-show'); state.partyPopper.setOptions((options) => ({ ...options, modifiers: [ ...options.modifiers, {name: 'eventListeners', enabled: false}, ], })); } } const clickOutside = (event) => { hidePopper(); } nextTick(async () => { const Popper = await import('../plugins/popper/popper.esm.js'); state.partyPopper = Popper.createPopper(state.picker.value, state.popper.value, { placement: props.direct, modifiers: [ { name: 'offset', options: { offset: [0, 8], }, }, { name: 'computeStyles', options: { gpuAcceleration: false, adaptive: false } } ], }); state.picker.value.addEventListener("focus", showPopper); }) onMounted(() => { // state.pickerColor.value = props.modelValue; state.sketchColor.value = {'hex': props.modelValue}; }); return {...state, clickOutside, pickerColorChange} } }; export const uploadModalComponent = { template: ``, props: { modalTitle: { type: String, default: "" }, modalShow: { type: Boolean, default: false }, modalFade: { type: Boolean, default: false }, uploadTip: { type: String, default: "" }, uploadAction: { type: String, default: "" }, uploadAllow: { type: Array, default: "" }, uploadCount: { type: [Number, String], default: 1 } }, setup(props, context) { const {modalShow, modalFade, uploadAllow} = toRefs(props); const state = { modal: ref(null), modalTitle: ref(""), uploadFile: ref(null), uploadTip: "", show: false, bsModal: {}, uploadLang: "zh" } watch(modalShow, () => { state.show = !state.show; if (state.show) state.bsModal.show(); else state.bsModal.hide(); }) const initBsModal = () => { state.bsModal = new bootstrap.Modal(state.modal.value); if (modalShow.value) { state.bsModal.show(); state.show = true; } else { state.bsModal.hide(); state.show = false; } state.modal.value.addEventListener('hide.bs.modal', () => { state.show = false; context.emit('update:modelShow', false); }); } const updateLangText = () => { const html = document.querySelector('html'); let lang = html.getAttribute('data-bs-language'); const [tip1, tip2] = props.uploadTip.split("&"); if (lang === "cn" || tip2 === undefined) state.uploadTip = tip1; else state.uploadTip = tip2; const [title1, title2] = props.modalTitle.split("&"); if (lang === "cn" || title2 === undefined) state.modalTitle.value = title1; else state.modalTitle.value = title2; state.uploadLang = lang; if (lang === "cn") state.uploadLang = "zh"; } const initUploadFile = () => { $(state.uploadFile.value).fileinput({ language: state.uploadLang, theme: "fa6", dropZoneTitle: state.uploadTip, showClose: false, browseClass: "btn btn-primary btn-df", allowedFileExtensions: uploadAllow.value, uploadUrl: props.uploadAction, maxFileCount: isNaN(Number(props.uploadCount)) ? 1 : Number(props.uploadCount) }); $(state.uploadFile.value).on('fileuploaded', function (event, data) { state.bsModal.hide(); state.show = false; $(state.uploadFile.value).fileinput('clear'); context.emit("upload-success", data) }); $(state.uploadFile.value).on('fileuploaderror', function (event, data, msg) { if (data.jqXHR.responseText) { var errMsg = eval(data.jqXHR.responseText); context.emit("upload-error", errMsg); } }); } onMounted(() => { const html = document.querySelector('html'); html.addEventListener("loaded", () => { updateLangText(); initBsModal(); initUploadFile(); }) const observer = new mutationObserver(() => { updateLangText(); }); const config = { attributes: true, attributeFilter: ["data-bs-language"], subtree: false }; observer.observe(html, config); }) return {...state, modalFade} } } export const upgradeModalComponent = { template: ``, props: { modalShow: { type: Boolean, default: false }, modalFade: { type: Boolean, default: true }, checkUpgrade: { type: Boolean, default: false }, patchSn: { type: String, default: "" } }, setup(props, context) { const {modalFade, checkUpgrade, patchSn} = toRefs(props); const state = { modal: ref(null), checkUpgrade: ref(false), systemPatchs: reactive([]), showLog: ref(false), showLogPatch: ref({}), hadUpdate: ref(false), updatePercent: ref(0), upgradePatch: ref({}), facAliase: "", bsModal: {}, } watchEffect(async () => { if (checkUpgrade.value) { let result = await func("/upgrade/checkHelpNet"); if (result.status === "error") { alertMsg(result.msg, "error"); return; } if (!patchSn.value) { result = await func("/upgrade/getSystemAliase"); if (result.status === "error") { alertMsg(result.msg, "error"); context.emit('update:checkUpgrade', false); return; } if (result.data.length === 0) { alertMsg("已经是最新版本It is the latest version", "success"); context.emit('update:checkUpgrade', false); return; } state.facAliase = result.data[0].aliase; result = await func("/upgrade/getAllSystemPatch"); if (result.status === "error") { alertMsg(result.msg, "error"); context.emit('update:checkUpgrade', false); return; } if (result.data.length === 0) { alertMsg("已经是最新版本It is the latest version", "success"); context.emit('update:checkUpgrade', false); return; } state.systemPatchs.splice(0); state.systemPatchs.push(...result.data); let hadImpact = false; for (let i = 0; i < state.systemPatchs.length; i++) { state.systemPatchs[i].allow = true; if (!hadImpact) { const impact = state.systemPatchs[i].impact; hadImpact = impact === "1"; } else { state.systemPatchs[i].allow = false; } } result = await func("/upgrade/checkVersionMaster"); if (result.status === "error") { alertMsg(result.msg, "error"); context.emit('update:checkUpgrade', false); return; } if (result.data === 0) { confirm({ title: '注意Tip', content: '设备可能升级过其他固件,如果继续升级,功能可能会被覆盖,是否继续?The device may have been upgraded with custom firmware, and the upgrade function may be overwritten. Do you want to continue?', buttons: { ok: { text: "继续Continue", btnClass: 'btn-primary', keys: ['enter'], action: () => { state.bsModal.show(); } }, cancel: { text: "取消Cancel", action: () => { context.emit('update:checkUpgrade', false); } } } }); } else { state.bsModal.show(); } } else { let result = await func("/upgrade/getSystemAliase"); if (result.status === "error") { alertMsg(result.msg, "error"); context.emit('update:checkUpgrade', false); return; } if (result.data.length === 0) { alertMsg("无效固件编号Invalid upgrade sn", "error"); context.emit('update:checkUpgrade', false); return; } state.facAliase = result.data[0].aliase; result = await func("/upgrade/getSystemPatchBySn", {"sn": patchSn.value}); if (result.data.length === 0) { alertMsg("无效固件编号Invalid upgrade sn", "error"); context.emit('update:checkUpgrade', false); return; } state.systemPatchs.splice(0); state.systemPatchs.push(...result.data); let hadImpact = false; for (let i = 0; i < state.systemPatchs.length; i++) { state.systemPatchs[i].allow = true; if (!hadImpact) { const impact = state.systemPatchs[i].impact; hadImpact = impact === "1"; } else { state.systemPatchs[i].allow = false; } } state.bsModal.show(); } } }) const handleVersionLogs = computed(() => { const regex = /[\r\n\t]/g; state.showLogPatch.value.description = state.showLogPatch.value.description.replace(regex, ""); return state.showLogPatch.value.description.split(";").filter((item) => { return item !== ""; }) }) const showPatchVersionLog = idx => { state.showLogPatch.value = state.systemPatchs[idx]; state.showLog.value = true; } const hidePatchVersionLog = () => { state.showLog.value = false; } const getUpdateFileSize = async name => { const params = { "action": "get_file_size", "name": name }; const data = await axios_post("/link/upgrade.php", params); return data.size; }; const handleUpdatePatch = idx => { if (state.hadUpdate.value) return; state.upgradePatch.value = state.systemPatchs[idx]; const patch = state.systemPatchs[idx]; const chip = patch.chip; let name = patch.name; let type = "update"; if (name.indexOf("_sn_") > 0) { name = name.replace("_sn_", "_" + state.facAliase + "_"); type = "sn"; } else { name = name.replace("_", "_" + state.facAliase + "_"); type = "update"; } const params = { action: "update", name: name, chip: chip, type: type } axios_post('/link/upgrade.php', params) .then(async data => { const total = Number(data.size); state.hadUpdate.value = true; state.updatePercent.value = 0; if (total > 0) { const timerId = setInterval(async function () { const size = await getUpdateFileSize(name); state.updatePercent.value = parseInt(size / total * 100); if (size >= total) { clearInterval(timerId); state.bsModal.hide(); context.emit('update:checkUpgrade', false); state.hadUpdate.value = false; state.upgradePatch.value = {}; rebootConfirm('下载完成,是否立即重启系统完成更新?'); } }, 1000); } }) } const handleDownloadPatch = idx => { state.upgradePatch.value = state.systemPatchs[idx]; const patch = state.systemPatchs[idx]; const chip = patch.chip; let name = patch.name; let type = "update"; if (name.indexOf("_sn_") > 0) { name = name.replace("_sn_", "_" + state.facAliase + "_"); type = "sn"; } else { name = name.replace("_", "_" + state.facAliase + "_"); type = "update"; } const url = "http://help.linkpi.cn:5735/upgrade/" + chip + "/" + type + "/" + name; const downName = ""; const a = document.createElement('a'); const e = document.createEvent('MouseEvents'); e.initEvent('click', false, false); a.href = url; a.download = downName; a.dispatchEvent(e); } onMounted(() => { state.bsModal = new bootstrap.Modal(state.modal.value); state.modal.value.addEventListener('hide.bs.modal', () => { context.emit('update:modelShow', false); context.emit('update:checkUpgrade', false); }); }) return { ...state, modalFade, handleVersionLogs, showPatchVersionLog, hidePatchVersionLog, handleUpdatePatch, handleDownloadPatch } } } export const customModalComponent = { template: ``, props: { modalSize: { type: String, default: "" }, hadHeader: { type: Boolean, default: true }, hadFooter: { type: Boolean, default: true }, modalTitle: { type: String, default: "标题" }, modalShow: { type: Boolean, default: false }, modalFade: { type: Boolean, default: true }, bodyClass: { type: String, default: "" }, contentClass: { type: String, default: "" }, confirmBtnName: { type: String, default: "确定" }, cancelBtnName: { type: String, default: "取消" }, hadConfirmBtn: { type: Boolean, default: true }, cancelCloseModal: { type: Boolean, default: true } }, setup(props, context) { const {modalShow, modalFade, modalTitle} = toRefs(props); const state = { modal: ref(null), modalTitle: ref(""), modalConfirmBtnName: ref("确定"), modalCancelBtnName: ref("取消"), show: false, bsModal: {}, } watch(modalShow, () => { if (Object.keys(state.bsModal).length === 0) return; state.show = !state.show; if (state.show) state.bsModal.show(); else state.bsModal.hide(); context.emit('modal-visible', state.show); }) watch(modalTitle, () => { updateLangText(); }) const initBsModal = () => { state.bsModal = new bootstrap.Modal(state.modal.value); if (modalShow.value) { state.bsModal.show(); state.show = true; } else { state.bsModal.hide(); state.show = false; } context.emit('modal-visible', state.show); state.modal.value.addEventListener('hide.bs.modal', () => { state.show = false; context.emit('update:modelShow', state.show); context.emit('modal-visible', state.show); }); } const confirmBtnClick = () => { context.emit("confirm-btn-click"); } const cancelBtnClick = () => { context.emit("cancel-btn-click"); } const updateLangText = () => { const html = document.querySelector('html'); let lang = html.getAttribute('data-bs-language'); if (props.modalTitle !== undefined) { const [title1, title2] = props.modalTitle.split("&"); if (lang === "cn" || title2 === undefined) state.modalTitle.value = title1; else state.modalTitle.value = title2; } if (props.confirmBtnName !== undefined) { const [name1, name2] = props.confirmBtnName.split("&"); if (lang === "cn" || name2 === undefined) state.modalConfirmBtnName.value = name1; else state.modalConfirmBtnName.value = name2; } if (props.cancelBtnName !== undefined) { const [name1, name2] = props.cancelBtnName.split("&"); if (lang === "cn" || name2 === undefined) state.modalCancelBtnName.value = name1; else state.modalCancelBtnName.value = name2; } } onMounted(() => { const html = document.querySelector('html'); html.addEventListener("loaded", () => { updateLangText(); initBsModal(); }) const observer = new mutationObserver(() => { updateLangText(); }); const config = { attributes: true, attributeFilter: ["data-bs-language"], subtree: false }; observer.observe(html, config); }) return {...state, modalFade, confirmBtnClick, cancelBtnClick} } } export const loadingButtonComponent = { template: ``, props: { customClass: { type: String, default: "" }, hadLoading: { type: Boolean, default: false } }, setup(props, context) { const onButtonClick = () => { context.emit("button-click", "click") } return {onButtonClick} } } export const ptzDirectComponent = { template: `
云台控制 PTZ
预置位 Preset
焦距 Zoom
`, props: { arrowClass: { type: String, default: "", }, homeClass: { type: String, default: "", }, gop: { type: Number, default: 5 }, sticks: { type: Array, default: ['left', 'left-up', 'up', 'right-up', 'right', 'right-down', 'down', 'left-down', 'home'] }, zoomVal: { type: Number, default: 0 }, zoomMin: { type: Number, default: 0 }, zoomMax: { type: Number, default: 10 }, zoomStep: { type: Number, default: 1 }, zoomFix: { type: Number, default: 0 } }, components: { "noui-slider": nouiSliderComponent }, setup(props, context) { const state = { zoom: ref(0), presetVal: ref(0) } watch(() => props.zoomVal, (newValue, oldValue) => { state.zoom.value = newValue; }) const handlePtzMove = type => { context.emit("ptz-move", type) } const updatePreset = val => { state.presetVal.value = val; } const onTouchSlideEnd = val => { context.emit('zoom-change', val); } const handleCallPreset = () => { context.emit('call-preset', state.presetVal.value); } const handleSetPreset = () => { context.emit('set-preset', state.presetVal.value); } return {...state, handlePtzMove, onTouchSlideEnd, updatePreset, handleCallPreset, handleSetPreset} } } export const usbOptionComponent = { template: `
`, setup(props, context) { const hadMountInfo = reactive({}); const hadMountDisk = ref(false); const {diskConf, handleDiskConf, updateDiskConf} = useDiskConf(); const unInstallDisk = () => { confirm({ title: '卸载磁盘UnInstall Disk', content: '是否卸载磁盘,请确保没有处于录制状态Whether to uninstall the disk, please make sure it is not in the recording state', buttons: { ok: { text: "卸载Confirm", btnClass: 'btn-primary', action: () => { func("/system/umountDisk").then(res => { alertMsg(res.msg, res.status); }) } }, cancel: { text: "取消Cancel", action: () => { } } } }); } const formatDisk = () => { confirm({ title: '格式化磁盘Formatted Disk', content: `
`, buttons: { ok: { text: "格式化Format", btnClass: 'btn-primary', action: () => { const formatPasswd = document.querySelector("#formatPasswd").value; func("/system/formatReady", {"psd": formatPasswd}).then(res => { return new Promise((resolve, reject) => { if (res.status === "error") { alertMsg(res.msg, res.status); reject(); return; } resolve(); }) }).then(() => { const diskFormat = document.querySelector("#diskFormat").value; const notify = alertMsg("正在格式化,请勿关闭此页面Do not close this page while formatting", "success", 99999999); func("/system/formatDisk", {"format": diskFormat}); let interval = setInterval(() => { func("/system/checkFormatProgress").then(res => { if (res.data === 0) { clearInterval(interval); notify.remove(); setTimeout(() => alertMsg(res.msg, res.status), 600); } }) }, 5000); }) } }, cancel: { text: "取消Cancel", action: () => { } } } }); } const checkMountDisk = () => { func("/system/getMountDiskSpace").then(res => { Object.assign(hadMountInfo, res.data); hadMountDisk.value = (res.status === "success"); }) setTimeout(checkMountDisk, 1000); } const turnMountDisk = () => { const html = document.querySelector("html"); const lang = html.getAttribute("data-bs-language"); const jc = confirm({ title: '磁盘挂载Mount Disk', content: `
`, buttons: { ok: { text: "挂载Mount", btnClass: 'btn-primary', action: () => { updateDiskConf({ enable: true, used: document.querySelector('#mount_device').value, shared: { ip: document.querySelector('#shared_ip').value, type: document.querySelector('#shared_protocol').value, path: document.querySelector('#shared_path').value, auth: { uname: document.querySelector('#shared_uname').value, passwd: document.querySelector('#shared_passwd').value, } }, local: { device: document.querySelector('#local_devices').value } }).then(async () => { handleDiskConf(); alertMsg("磁盘检测中,请稍后...Disk checking, please wait...", "success"); const result = await func("/system/mountDisk"); if (result.status === "success") jc.close(); setTimeout(() => alertMsg(result.msg, result.status), 600); }); return false; } }, cancel: { text: "取消Cancel", action: () => { } } }, onOpenBefore: () => { const display = (type, protocol) => { const shareElements = document.querySelectorAll('.share-device'); const localElements = document.querySelectorAll('.local-device'); shareElements.forEach(element => element.style.display = 'none'); localElements.forEach(element => element.style.display = 'none'); if (type === "shared") { shareElements.forEach(element => element.style.display = ''); const cifsAuthElements = document.querySelectorAll('.cifs-auth'); document.querySelector('#shared_protocol').value = protocol; cifsAuthElements.forEach(element => element.style.display = 'none'); if (protocol === 'cifs') cifsAuthElements.forEach(element => element.style.display = ''); return; } localElements.forEach(element => element.style.display = ''); } func("/system/getLocalDisk").then(result => { const html = document.querySelector("html"); const lang = html.getAttribute("data-bs-language"); result.data.forEach(item => { const option = document.createElement('option'); option.value = item.name; if (item.name === "/dev/mmcblk0p6") { if (lang === "cn") item.name = "内部存储"; else item.name = "device storage"; } option.text = item.name + "( " + item.size + " )"; document.querySelector('#local_devices').add(option); }) }) document.querySelector('#mount_device').value = diskConf.used; document.querySelector('#local_devices').value = diskConf.local.device; document.querySelector('#shared_protocol').value = diskConf.shared.type; document.querySelector('#shared_uname').value = diskConf.shared.auth.uname; document.querySelector('#shared_passwd').value = diskConf.shared.auth.passwd; document.querySelector('#shared_ip').value = diskConf.shared.ip; document.querySelector('#shared_path').value = diskConf.shared.path; display(diskConf.used, diskConf.shared.type); document.querySelector('#mount_device').addEventListener('change', () => { const type = document.querySelector('#mount_device').value; display(type, "cifs"); }); document.querySelector('#shared_protocol').addEventListener('change', () => { const protocol = document.querySelector('#shared_protocol').value; display("shared", protocol); }); } }); } onMounted(checkMountDisk); return {hadMountInfo, hadMountDisk, diskConf, unInstallDisk, formatDisk, turnMountDisk} } }