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: `
`,
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: `
Tip:级别标记为重要的升级包不能跳过,更新之后才能继续更新。
Tip:Upgrade packages marked as impact cannot be skipped and can only be updated after they have been updated
`,
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: ``,
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}
}
}