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 { func, confirm, rebootConfirm, alertMsg, axios_post, isEmpty,formatTime } 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 highlightTextPlugin = { install(app, options) { //const urlParam = options.urlParam || 'highlight'; //const urlValue = new URLSearchParams(window.location.search).get(urlParam); const urlValue = "预" if (urlValue) { nextTick(()=>{ const elements = document.querySelectorAll('main cn, main en'); elements.forEach((el) => { const textContent = el.textContent; const startIndex = textContent.indexOf(urlValue); if (startIndex !== -1) { const endIndex = startIndex + urlValue.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; } }) }) } } }; 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: ['value','color'], setup(props, context) { const tmp_mask = ref(null); const tmp_text = ref(null); const { value } = toRefs(props); watch(value,()=>{ tmp_mask.value.style.bottom = props.value + '%'; tmp_text.value.textContent = props.value + '℃'; }) onMounted(()=>{ tmp_mask.value.parentElement.style.background = props.color; }) 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','direct'], 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','modalShow','modalFade','uploadTip',"uploadAction",'uploadAllow','uploadCount'], setup(props,context) { const { modalShow,modalFade } = 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: eval('('+props.uploadAllow+')'), 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(); }) }) return { ...state,modalFade } } } export const upgradeModalComponent = { template: ``, props:['modalShow','modalFade','checkUpgrade','patchSn'], 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); 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); 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 params = { action:"download", name:name, chip:chip, type:type } // const fileName = name; // axios_post('/link/upgrade.php',params, { responseType: 'arraybuffer' }) // .then(data => { // const blob = new Blob([data], { type: 'application/octet-stream' }); // const url = URL.createObjectURL(blob); // const a = document.createElement('a'); // a.href = url; // a.download = fileName; // document.body.appendChild(a); // a.click(); // document.body.removeChild(a); // URL.revokeObjectURL(url); // }) 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,() => { 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','hadLoading'], setup(props,context) { const { hadLoading } = toRefs(props); const onButtonClick = () => { context.emit("button-click","click") } return { hadLoading,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) => { console.log(newValue); 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 } } }