linkpi_firmware_history/rootfs/link/webflex/assets/js/vue.helper.js

2303 lines
94 KiB
JavaScript
Raw Permalink Normal View History

2024-02-15 22:07:50 +01:00
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'
2024-02-15 22:07:54 +01:00
import { md5 } from "../plugins/md5/js.md5.esm.js";
import { func, confirm, rebootConfirm, alertMsg, axios_post, isEmpty, formatTime, clearReactiveArray, getUrlParam } from './lp.utils.js'
2024-02-15 22:07:50 +01:00
import { useDiskConf } from "./vue.hooks.js";
2024-02-15 22:07:54 +01:00
const { ref, reactive, toRefs, watch, watchEffect, computed, onMounted, nextTick, defineAsyncComponent } = vue;
2024-02-15 22:07:50 +01:00
2024-02-15 22:07:54 +01:00
export const ignoreCustomElementPlugin = {
2024-02-15 22:07:50 +01:00
install: (app) => {
app.config.compilerOptions.isCustomElement = (tag) => tag === 'cn' || tag === 'en';
}
};
2024-02-15 22:07:54 +01:00
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);
// })
}
})
2024-02-15 22:07:50 +01:00
}
};
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: `<div class="pie">
<div class="temperature">
<div class="bar">
<div class="mask" ref="tmp_mask"></div>
<span class="percent" ref="tmp_text">0</span>
</div>
</div>
</div>`,
2024-02-15 22:07:52 +01:00
props: {
modelValue: {
type: Number,
default: 0
},
activeColor: {
type: String,
default: "#fb0"
}
},
2024-02-15 22:07:50 +01:00
setup(props, context) {
const tmp_mask = ref(null);
const tmp_text = ref(null);
2024-02-15 22:07:54 +01:00
const {modelValue, activeColor} = toRefs(props);
2024-02-15 22:07:50 +01:00
2024-02-15 22:07:54 +01:00
watch(modelValue, () => {
2024-02-15 22:07:52 +01:00
tmp_mask.value.style.bottom = modelValue.value + '%';
tmp_text.value.textContent = modelValue.value + '℃';
2024-02-15 22:07:50 +01:00
})
2024-02-15 22:07:52 +01:00
onMounted(() => tmp_mask.value.parentElement.style.background = activeColor.value);
2024-02-15 22:07:50 +01:00
2024-02-15 22:07:54 +01:00
return {tmp_mask, tmp_text}
2024-02-15 22:07:50 +01:00
}
};
export const statusPieChartComponent = {
template: `<div class="pie">
<div class="chart" ref="pie_chart"></div>
<span class="percent" ref="pie_text"></span>
</div>`,
props: {
modelValue: {
2024-02-15 22:07:54 +01:00
type: Number,
default: 0
2024-02-15 22:07:50 +01:00
},
activeColor: {
type: String,
default: "#fb0"
},
trackColor: {
type: String,
default: "#777"
}
},
setup(props, context) {
const pie_chart = ref(null);
const pie_text = ref(null);
2024-02-15 22:07:54 +01:00
const {modelValue} = toRefs(props);
2024-02-15 22:07:50 +01:00
2024-02-15 22:07:54 +01:00
watch(modelValue, newValue => {
if ($(pie_chart.value).data('easyPieChart'))
$(pie_chart.value).data('easyPieChart').update(newValue);
2024-02-15 22:07:50 +01:00
})
2024-02-15 22:07:54 +01:00
onMounted(() => {
2024-02-15 22:07:50 +01:00
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) + "%";
}
});
})
2024-02-15 22:07:54 +01:00
return {pie_chart, pie_text}
2024-02-15 22:07:50 +01:00
}
};
export const netFlotChartComponent = {
template: `<div class="col-lg-12 netState" ref="net_chart"> </div>`,
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,
2024-02-15 22:07:54 +01:00
default: "#eee"
2024-02-15 22:07:50 +01:00
},
borderColor: {
type: String,
default: "#ccc"
},
2024-02-15 22:07:54 +01:00
tipBorderColor: {
2024-02-15 22:07:50 +01:00
type: String,
default: "#fb0"
},
tipBgColor: {
type: String,
default: "#fff"
},
tipTxtColor: {
type: String,
default: "#555"
}
},
2024-02-15 22:07:54 +01:00
setup(props, context) {
2024-02-15 22:07:50 +01:00
const net_chart = ref(null);
let plot = {};
const showTooltip = (x, y, color, contents) => {
$('<div id="tooltip">' + contents + '</div>').css({
position: 'absolute',
display: 'none',
top: y - 40,
left: x - 120,
border: '2px solid ' + props.tipBorderColor,
padding: '3px',
'font-size': '9px',
'border-radius': '5px',
2024-02-15 22:07:54 +01:00
'color': props.tipTxtColor,
2024-02-15 22:07:50 +01:00
'background-color': props.tipBgColor,
'font-family': 'Verdana, Arial, Helvetica, Tahoma, sans-serif',
opacity: 0.9
}).appendTo("body").fadeIn(200);
}
const initPlot = () => {
2024-02-15 22:07:54 +01:00
if (Object.keys(plot).length === 0) {
2024-02-15 22:07:50 +01:00
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,
2024-02-15 22:07:54 +01:00
tickFormatter: (v, axis) => {
if (axis.max < 1024)
2024-02-15 22:07:50 +01:00
return v + "Kb/s";
else {
v /= 1024;
2024-02-15 22:07:54 +01:00
if (axis.max < 10240)
return v.toFixed(2) + "Mb/s";
2024-02-15 22:07:50 +01:00
else
2024-02-15 22:07:54 +01:00
return Math.floor(v) + "Mb/s";
2024-02-15 22:07:50 +01:00
}
}
},
xaxis: {
show: false
},
grid: {
hoverable: true,
clickable: true,
tickColor: props.tickColor,
borderWidth: 1,
borderColor: props.borderColor,
},
2024-02-15 22:07:54 +01:00
colors: [props.line1Color, props.line2Color],
2024-02-15 22:07:50 +01:00
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];
2024-02-15 22:07:54 +01:00
if (data > 1024)
data = parseInt(data / 1024) + "Mb/s";
2024-02-15 22:07:50 +01:00
else
data += "kb/s";
if (item.seriesIndex === 0)
showTooltip(item.pageX + 100, item.pageY - 10, color, "<cn>上行</cn><en>upward</en>: " + data);
if (item.seriesIndex === 1)
showTooltip(item.pageX + 100, item.pageY - 10, color, "<cn>下行</cn><en>downward</en>: " + data);
}
2024-02-15 22:07:54 +01:00
} else {
2024-02-15 22:07:50 +01:00
prePoint = null;
preLabel = null;
$(this).css({
"cursor": "auto"
});
$("#tooltip").remove();
}
});
}
$(net_chart.value).tooltip();
}
}
const updatePlot = () => {
2024-02-15 22:07:54 +01:00
if (Object.keys(plot).length !== 0) {
2024-02-15 22:07:50 +01:00
let maxy = props.maxy;
let data1 = props.data1;
let data2 = props.data2;
plot.setData([data1, data2]);
plot.draw();
2024-02-15 22:07:54 +01:00
plot.getOptions().yaxes[0].max = maxy;
plot.getOptions().yaxes[0].tickSize = Math.floor(maxy / 5);
2024-02-15 22:07:50 +01:00
plot.setupGrid();
}
}
2024-02-15 22:07:54 +01:00
watch(props.data1, () => {
2024-02-15 22:07:50 +01:00
updatePlot();
2024-02-15 22:07:54 +01:00
}, {deep: true})
2024-02-15 22:07:50 +01:00
2024-02-15 22:07:54 +01:00
onMounted(() => {
setTimeout(initPlot, 100);
2024-02-15 22:07:50 +01:00
})
return {net_chart}
}
}
export const bootstrapSwitchComponent = {
template: `<input type="checkbox" class="switch form-control" ref="bs_switch">`,
props: {
modelValue: {
type: Boolean,
default: false
},
size: {
type: String,
2024-02-15 22:07:54 +01:00
default: "small" //normal
2024-02-15 22:07:50 +01:00
}
},
setup(props, context) {
2024-02-15 22:07:54 +01:00
const {modelValue, size} = toRefs(props);
2024-02-15 22:07:50 +01:00
const bs_switch = ref(null);
2024-02-15 22:07:54 +01:00
watch(modelValue, () => {
2024-02-15 22:07:50 +01:00
$(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);
}
})
})
2024-02-15 22:07:54 +01:00
return {bs_switch}
2024-02-15 22:07:50 +01:00
}
};
export const multipleSelectComponent = {
template: `<select class="form-select" v-model="selectValue" @change="onSelectChange">
<slot></slot>
</select>`,
props: {
value1: {
2024-02-15 22:07:54 +01:00
type: [Number, String, Boolean],
default: 0
2024-02-15 22:07:50 +01:00
},
value2: {
2024-02-15 22:07:54 +01:00
type: [Number, String, Boolean],
2024-02-15 22:07:50 +01:00
default: 0
},
split: {
type: String,
default: 0
}
},
2024-02-15 22:07:54 +01:00
setup(props, context) {
2024-02-15 22:07:50 +01:00
let selectValue = ref("");
2024-02-15 22:07:54 +01:00
watchEffect(() => {
2024-02-15 22:07:50 +01:00
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);
};
2024-02-15 22:07:54 +01:00
const onSelectChange = () => {
2024-02-15 22:07:50 +01:00
let [value1, value2] = selectValue.value.split(props.split);
context.emit('update:value1', parseValue(value1));
context.emit('update:value2', parseValue(value2));
}
2024-02-15 22:07:54 +01:00
onMounted(() => {
2024-02-15 22:07:50 +01:00
selectValue.value = props.value1 + props.split + props.value2;
})
2024-02-15 22:07:54 +01:00
return {selectValue, onSelectChange}
2024-02-15 22:07:50 +01:00
}
};
export const multipleInputComponent = {
template: `<input type="text" class="form-control" v-model="selectValue" @change="onInputChange">`,
props: {
value1: {
2024-02-15 22:07:54 +01:00
type: [Number, String],
2024-02-15 22:07:50 +01:00
default: 0
},
value2: {
2024-02-15 22:07:54 +01:00
type: [Number, String],
2024-02-15 22:07:50 +01:00
default: 0
},
split: {
type: String,
default: 0
}
},
2024-02-15 22:07:54 +01:00
setup(props, context) {
2024-02-15 22:07:50 +01:00
let selectValue = ref("");
2024-02-15 22:07:54 +01:00
const {value1, value2} = toRefs(props);
watchEffect(() => {
2024-02-15 22:07:50 +01:00
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;
})
2024-02-15 22:07:54 +01:00
const onInputChange = () => {
let [val1, val2] = selectValue.value.split(props.split);
2024-02-15 22:07:50 +01:00
val1 = isNaN(Number(val1)) ? val1 : Number(val1);
val2 = isNaN(Number(val2)) ? val2 : Number(val2);
2024-02-15 22:07:54 +01:00
if (typeof val1 === "string")
2024-02-15 22:07:50 +01:00
val1 = val1.trim();
2024-02-15 22:07:54 +01:00
if (typeof val2 === "string")
2024-02-15 22:07:50 +01:00
val2 = val2.trim();
context.emit('update:value1', val1);
context.emit('update:value2', val2);
selectValue.value = val1 + props.split + val2;
}
2024-02-15 22:07:54 +01:00
return {selectValue, onInputChange}
2024-02-15 22:07:50 +01:00
}
};
export const nouiSliderComponent = {
template: `<div class="slider-wrap lp-cursor-pointer" ref="slider"></div>`,
props: {
modelValue: {
2024-02-15 22:07:54 +01:00
type: [Number, String],
default: 0
2024-02-15 22:07:50 +01:00
},
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
}
},
2024-02-15 22:07:54 +01:00
setup(props, context) {
2024-02-15 22:07:50 +01:00
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 => {
2024-02-15 22:07:54 +01:00
if (isEmpty(props.format)) {
if (props.fix === 0)
2024-02-15 22:07:50 +01:00
value = parseInt(value);
else
value = parseFloat(value).toFixed(props.fix);
}
2024-02-15 22:07:54 +01:00
if (props.format === "time")
2024-02-15 22:07:50 +01:00
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},
});
2024-02-15 22:07:54 +01:00
if (!hover)
2024-02-15 22:07:50 +01:00
hideTooltip();
}
});
watch(() => props.funcValue, (newValue, oldValue) => {
if (slider.value && !isSlide) {
slider.value.noUiSlider.updateOptions({
start: newValue,
range: {'min': props.min, 'max': props.max},
});
2024-02-15 22:07:54 +01:00
if (!hover)
2024-02-15 22:07:50 +01:00
hideTooltip();
}
});
watch(() => props.max, (newValue, oldValue) => {
if (slider.value && !isSlide) {
slider.value.noUiSlider.updateOptions({
range: {'min': props.min, 'max': newValue},
});
2024-02-15 22:07:54 +01:00
if (!hover)
2024-02-15 22:07:50 +01:00
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,
});
2024-02-15 22:07:54 +01:00
if (props.disable)
2024-02-15 22:07:50 +01:00
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]));
2024-02-15 22:07:54 +01:00
context.emit('slide-end', formatTooltipValue(values[mark]), props.index);
2024-02-15 22:07:50 +01:00
});
})
2024-02-15 22:07:54 +01:00
return {slider}
2024-02-15 22:07:50 +01:00
}
};
export const h5PlayerComponent = {
template: `<div style="width:100%; padding-bottom: 56.25%; position: relative;">
<video autoplay controls muted style="width:100%;height: 100%; position: absolute; background: #555;" ref="videoHandler"></video>
<div style="position: absolute;width: 100%;height: 100%" ref="jessHandler"></div>
<div class="lp-video-cloud" ref="cloudHandler">
<div class="loading"></div>
</div>
</div>`,
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
}
},
2024-02-15 22:07:54 +01:00
setup(props, context) {
2024-02-15 22:07:50 +01:00
2024-02-15 22:07:54 +01:00
const {url, codec, audio, buffer, canplay} = toRefs(props);
2024-02-15 22:07:50 +01:00
const state = {
videoHandler: ref(null),
jessHandler: ref(null),
cloudHandler: ref(null),
hadInitPlayer: false,
flvJsModule: {},
h5Player: {},
}
2024-02-15 22:07:54 +01:00
watchEffect(() => {
if (canplay.value) {
if (url.value !== "") {
if (state.hadInitPlayer)
2024-02-15 22:07:50 +01:00
destroyPlayer();
2024-02-15 22:07:54 +01:00
setTimeout(initPlayer, 300);
2024-02-15 22:07:50 +01:00
}
} else {
2024-02-15 22:07:54 +01:00
if (state.hadInitPlayer)
2024-02-15 22:07:50 +01:00
destroyPlayer();
}
})
const initPlayer = async () => {
2024-02-15 22:07:54 +01:00
if (props.url === "")
2024-02-15 22:07:50 +01:00
return;
2024-02-15 22:07:54 +01:00
if (props.codec === "h265") {
2024-02-15 22:07:50 +01:00
state.videoHandler.value.style.display = 'none';
state.jessHandler.value.style.display = 'block';
2024-02-15 22:07:54 +01:00
if (!window.Jessibuca)
2024-02-15 22:07:50 +01:00
await import('../plugins/jessibuca/jessibuca.js');
2024-02-15 22:07:54 +01:00
state.h5Player = new Jessibuca({
2024-02-15 22:07:50 +01:00
container: state.jessHandler.value,
2024-02-15 22:07:54 +01:00
videoBuffer: buffer.value / 1000,
2024-02-15 22:07:50 +01:00
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';
2024-02-15 22:07:54 +01:00
if (!window.flvjs)
2024-02-15 22:07:50 +01:00
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();
2024-02-15 22:07:54 +01:00
state.videoHandler.value.addEventListener("canplay", () => {
2024-02-15 22:07:50 +01:00
state.cloudHandler.value.style.display = 'none'
});
}
state.hadInitPlayer = true;
}
const destroyPlayer = () => {
2024-02-15 22:07:54 +01:00
if (Object.keys(state.h5Player).length > 0) {
if (state.h5Player.hasOwnProperty("unload")) {
2024-02-15 22:07:50 +01:00
state.h5Player.unload();
state.h5Player.detachMediaElement();
}
state.h5Player.destroy();
state.h5Player = {};
}
state.cloudHandler.value.style.display = 'flex';
2024-02-15 22:07:54 +01:00
state.videoHandler.value.removeEventListener("canplay", () => {
});
2024-02-15 22:07:50 +01:00
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);
2024-02-15 22:07:54 +01:00
return {...state}
2024-02-15 22:07:50 +01:00
}
};
export const videoPlayerComponent = {
template: `<div style="width:100%; padding-bottom: 56.25%; position: relative;">
<video autoplay controls muted style="width:100%;height: 100%; position: absolute; background: #555;" ref="videoHandler"></video>
<div class="lp-video-cloud" ref="cloudHandler">
<div class="loading"></div>
</div>
</div>`,
props: {
url: {
type: String,
default: ""
},
canplay: {
type: Boolean,
default: true
}
},
2024-02-15 22:07:54 +01:00
setup(props, context) {
2024-02-15 22:07:50 +01:00
2024-02-15 22:07:54 +01:00
const {url, canplay} = toRefs(props);
2024-02-15 22:07:50 +01:00
const state = {
videoHandler: ref(null),
cloudHandler: ref(null),
hadInitPlayer: false,
}
2024-02-15 22:07:54 +01:00
watchEffect(() => {
if (canplay.value) {
if (url.value !== "") {
if (state.hadInitPlayer)
2024-02-15 22:07:50 +01:00
destroyPlayer();
2024-02-15 22:07:54 +01:00
setTimeout(initPlayer, 300);
2024-02-15 22:07:50 +01:00
}
} else {
2024-02-15 22:07:54 +01:00
if (state.hadInitPlayer)
2024-02-15 22:07:50 +01:00
destroyPlayer();
}
})
const initPlayer = () => {
2024-02-15 22:07:54 +01:00
if (url.value === "")
2024-02-15 22:07:50 +01:00
return;
state.videoHandler.value.style.display = 'block';
state.videoHandler.value.src = url.value;
state.videoHandler.value.play();
2024-02-15 22:07:54 +01:00
state.videoHandler.value.addEventListener("canplay", () => {
2024-02-15 22:07:50 +01:00
state.cloudHandler.value.style.display = 'none'
});
state.hadInitPlayer = true;
}
2024-02-15 22:07:54 +01:00
2024-02-15 22:07:50 +01:00
const destroyPlayer = () => {
state.cloudHandler.value.style.display = 'flex';
2024-02-15 22:07:54 +01:00
state.videoHandler.value.removeEventListener("canplay", () => {
});
2024-02-15 22:07:50 +01:00
state.hadInitPlayer = false;
}
2024-02-15 22:07:54 +01:00
return {...state}
2024-02-15 22:07:50 +01:00
}
};
export const timepickerComponent = {
template: `<div class="input-group bootstrap-timepicker">
<input type="text" class="form-control" ref="timepicker">
<span class="input-group-text input-group-addon"><i class="fa-regular fa-clock"></i></span>
</div>`,
props: {
modelValue: {
type: String,
default: "00:00"
}
},
2024-02-15 22:07:54 +01:00
setup(props, context) {
2024-02-15 22:07:50 +01:00
const timepicker = ref(null);
2024-02-15 22:07:54 +01:00
const {modelValue} = toRefs(props);
2024-02-15 22:07:50 +01:00
2024-02-15 22:07:54 +01:00
watch(modelValue, () => {
2024-02-15 22:07:50 +01:00
$(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: `<div class="color-picker" v-click-outside="clickOutside">
<input class="form-control" type="text" v-model.trim.lazy="pickerColor" ref="picker" @change="pickerColorChange">
<div ref="popper" class="popper">
<sketch-picker v-model="sketchColor"></sketch-picker>
<div class="arrow" data-popper-arrow></div>
</div>
</div>`,
2024-02-15 22:07:52 +01:00
props: {
modelValue: {
type: String,
default: ""
},
direct: {
type: String,
default: "bottom"
}
},
2024-02-15 22:07:50 +01:00
components: {
"sketch-picker": defineAsyncComponent(() => {
return import('../plugins/vueColor/vue3.color.esm.js').then(module => {
2024-02-15 22:07:54 +01:00
const {Sketch} = module;
2024-02-15 22:07:50 +01:00
return Sketch;
})
}),
},
directives: {
"click-outside": clickOutsideDirective
},
2024-02-15 22:07:54 +01:00
setup(props, context) {
2024-02-15 22:07:50 +01:00
const state = {
picker: ref(null),
popper: ref(null),
pickerColor: ref(""),
sketchColor: ref(""),
partyPopper: {},
popperOptions: reactive({}),
}
2024-02-15 22:07:54 +01:00
watch(state.sketchColor, () => {
if (state.sketchColor.value.hasOwnProperty("hex")) {
2024-02-15 22:07:50 +01:00
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,
2024-02-15 22:07:54 +01:00
{name: 'eventListeners', enabled: true},
2024-02-15 22:07:50 +01:00
],
}));
state.partyPopper.update();
}
const hidePopper = () => {
2024-02-15 22:07:54 +01:00
if (Object.keys(state.partyPopper).length > 0) {
2024-02-15 22:07:50 +01:00
state.popper.value.removeAttribute('data-show');
state.partyPopper.setOptions((options) => ({
...options,
modifiers: [
...options.modifiers,
2024-02-15 22:07:54 +01:00
{name: 'eventListeners', enabled: false},
2024-02-15 22:07:50 +01:00
],
}));
}
}
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);
})
2024-02-15 22:07:54 +01:00
onMounted(() => {
2024-02-15 22:07:50 +01:00
// state.pickerColor.value = props.modelValue;
2024-02-15 22:07:54 +01:00
state.sketchColor.value = {'hex': props.modelValue};
2024-02-15 22:07:50 +01:00
});
2024-02-15 22:07:54 +01:00
return {...state, clickOutside, pickerColorChange}
2024-02-15 22:07:50 +01:00
}
};
export const uploadModalComponent = {
template: `<div :class="['modal',{'fade':modalFade===undefined ? false : JSON.parse(modalFade)}]" tabindex="-1" aria-hidden="true" ref="modal">
<div class="modal-dialog modal-lg modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">{{modalTitle}}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<input type="file" ref="uploadFile" name="uploadFile" multiple />
</div>
</div>
</div>
</div>`,
2024-02-15 22:07:54 +01:00
props: {
2024-02-15 22:07:52 +01:00
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: {
2024-02-15 22:07:54 +01:00
type: [Number, String],
2024-02-15 22:07:52 +01:00
default: 1
}
},
2024-02-15 22:07:54 +01:00
setup(props, context) {
2024-02-15 22:07:50 +01:00
2024-02-15 22:07:54 +01:00
const {modalShow, modalFade, uploadAllow} = toRefs(props);
2024-02-15 22:07:50 +01:00
const state = {
2024-02-15 22:07:54 +01:00
modal: ref(null),
modalTitle: ref(""),
2024-02-15 22:07:50 +01:00
uploadFile: ref(null),
uploadTip: "",
2024-02-15 22:07:54 +01:00
show: false,
bsModal: {},
2024-02-15 22:07:50 +01:00
uploadLang: "zh"
}
2024-02-15 22:07:54 +01:00
watch(modalShow, () => {
2024-02-15 22:07:50 +01:00
state.show = !state.show;
2024-02-15 22:07:54 +01:00
if (state.show)
2024-02-15 22:07:50 +01:00
state.bsModal.show();
else
state.bsModal.hide();
})
const initBsModal = () => {
state.bsModal = new bootstrap.Modal(state.modal.value);
2024-02-15 22:07:54 +01:00
if (modalShow.value) {
2024-02-15 22:07:50 +01:00
state.bsModal.show();
state.show = true;
} else {
state.bsModal.hide();
state.show = false;
}
2024-02-15 22:07:54 +01:00
state.modal.value.addEventListener('hide.bs.modal', () => {
2024-02-15 22:07:50 +01:00
state.show = false;
context.emit('update:modelShow', false);
});
}
const updateLangText = () => {
const html = document.querySelector('html');
let lang = html.getAttribute('data-bs-language');
2024-02-15 22:07:54 +01:00
const [tip1, tip2] = props.uploadTip.split("&");
if (lang === "cn" || tip2 === undefined)
2024-02-15 22:07:50 +01:00
state.uploadTip = tip1;
else
state.uploadTip = tip2;
2024-02-15 22:07:54 +01:00
const [title1, title2] = props.modalTitle.split("&");
if (lang === "cn" || title2 === undefined)
2024-02-15 22:07:50 +01:00
state.modalTitle.value = title1;
else
state.modalTitle.value = title2;
state.uploadLang = lang;
2024-02-15 22:07:54 +01:00
if (lang === "cn")
2024-02-15 22:07:50 +01:00
state.uploadLang = "zh";
}
const initUploadFile = () => {
$(state.uploadFile.value).fileinput({
language: state.uploadLang,
theme: "fa6",
dropZoneTitle: state.uploadTip,
showClose: false,
2024-02-15 22:07:54 +01:00
browseClass: "btn btn-primary btn-df",
2024-02-15 22:07:52 +01:00
allowedFileExtensions: uploadAllow.value,
2024-02-15 22:07:50 +01:00
uploadUrl: props.uploadAction,
maxFileCount: isNaN(Number(props.uploadCount)) ? 1 : Number(props.uploadCount)
});
2024-02-15 22:07:54 +01:00
$(state.uploadFile.value).on('fileuploaded', function (event, data) {
2024-02-15 22:07:50 +01:00
state.bsModal.hide();
state.show = false;
$(state.uploadFile.value).fileinput('clear');
2024-02-15 22:07:54 +01:00
context.emit("upload-success", data)
2024-02-15 22:07:50 +01:00
});
2024-02-15 22:07:54 +01:00
$(state.uploadFile.value).on('fileuploaderror', function (event, data, msg) {
if (data.jqXHR.responseText) {
2024-02-15 22:07:50 +01:00
var errMsg = eval(data.jqXHR.responseText);
2024-02-15 22:07:54 +01:00
context.emit("upload-error", errMsg);
2024-02-15 22:07:50 +01:00
}
});
}
2024-02-15 22:07:54 +01:00
onMounted(() => {
2024-02-15 22:07:50 +01:00
const html = document.querySelector('html');
2024-02-15 22:07:54 +01:00
html.addEventListener("loaded", () => {
2024-02-15 22:07:50 +01:00
updateLangText();
initBsModal();
initUploadFile();
})
2024-02-15 22:07:52 +01:00
const observer = new mutationObserver(() => {
updateLangText();
});
const config = {
attributes: true,
attributeFilter: ["data-bs-language"],
subtree: false
};
observer.observe(html, config);
2024-02-15 22:07:50 +01:00
})
2024-02-15 22:07:54 +01:00
return {...state, modalFade}
2024-02-15 22:07:50 +01:00
}
}
export const upgradeModalComponent = {
template: `<div :class="['modal',{'fade':modalFade===undefined ? false : JSON.parse(modalFade)}]" data-bs-backdrop="static" tabindex="-1" aria-hidden="true" ref="modal">
<div class="modal-dialog modal-lg modal-dialog-centered">
<div :class="['modal-content front',{'front0':!showLog},{'front180':showLog}]">
<div class="modal-header">
<h5 class="modal-title">
<cn>升级包</cn>
<en>Upgrade</en>
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body px-3">
<table class="table table-bordered">
<thead>
<tr>
<th>
<cn>序号</cn>
<en>Num</en>
</th>
<th>
<cn>名称</cn>
<en>Name</en>
</th>
<th>
<cn>版本</cn>
<en>Build</en>
</th>
<th>
<cn>日期</cn>
<en>Date</en>
</th>
<th>
<cn>级别</cn>
<en>Impact</en>
</th>
<th>
<cn>日志</cn>
<en>Log</en>
</th>
<th>
<cn>操作</cn>
<en>Option</en>
</th>
<th>
<cn>下载</cn>
<en>Download</en>
</th>
</tr>
</thead>
<tbody>
<tr v-for="(item,index) in systemPatchs" :key="item.id">
<td>{{index+1}}</td>
<td>{{item.name}}</td>
<td>{{item.build}}</td>
<td>{{item.sys_ver}}</td>
<td v-if="item.impact === '1'" class="lp-color-red">
<cn>重要</cn>
<en>impact</en>
</td>
<td v-else>
<cn>普通</cn>
<en>normal</en>
</td>
<td>
<a class="lp-cursor-pointer" @click="showPatchVersionLog(index)">
<cn>更新日志</cn>
<en>Show logs</en>
</a>
</td>
<td>
2024-02-15 22:07:52 +01:00
<a v-if="item.allow" class="lp-cursor-pointer" @click="handleUpdatePatch(index)">
2024-02-15 22:07:50 +01:00
<div v-if="upgradePatch.id === item.id && hadUpdate">{{updatePercent}}%</div>
<div v-else>
<cn>更新</cn>
<en>Update</en>
</div>
</a>
2024-02-15 22:07:52 +01:00
<a v-else>/</a>
2024-02-15 22:07:50 +01:00
</td>
<td>
2024-02-15 22:07:52 +01:00
<a v-if="item.allow" class="lp-cursor-pointer" @click="handleDownloadPatch(index)">
2024-02-15 22:07:50 +01:00
<cn>下载</cn>
<en>Download</en>
</a>
2024-02-15 22:07:52 +01:00
<a v-else>/</a>
2024-02-15 22:07:50 +01:00
</td>
</tr>
</tbody>
</table>
<div class="row mt-4 mb-2">
<div class="col-lg-12">
<cn>Tip级别标记为<cn style="color: red">重要</cn></cn>
<en>TipUpgrade packages marked as <en style="color: red">impact</en> cannot be skipped and can only be updated after they have been updated</en>
</div>
</div>
</div>
</div>
<div :class="['modal-content rear',{'rear180':!showLog},{'rear0':showLog}]">
<div v-if="Object.keys(showLogPatch).length > 0">
<div class="modal-header">
<h5 class="modal-title">
Build {{showLogPatch.sys_ver}}
</h5>
<button type="button" class="btn-close" @click="hidePatchVersionLog"></button>
</div>
<div class="modal-body">
<ul>
<li class="mt-2" v-for="(it,idx) in handleVersionLogs" :key="idx" style="font-size: 15px;white-space:pre-wrap;">{{it}}</li>
</ul>
</div>
</div>
</div>
</div>
</div>`,
2024-02-15 22:07:52 +01:00
props: {
modalShow: {
type: Boolean,
default: false
},
modalFade: {
2024-02-15 22:07:54 +01:00
type: Boolean,
2024-02-15 22:07:52 +01:00
default: true
},
checkUpgrade: {
type: Boolean,
default: false
},
patchSn: {
type: String,
2024-02-15 22:07:54 +01:00
default: ""
2024-02-15 22:07:52 +01:00
}
},
2024-02-15 22:07:54 +01:00
setup(props, context) {
2024-02-15 22:07:50 +01:00
2024-02-15 22:07:54 +01:00
const {modalFade, checkUpgrade, patchSn} = toRefs(props);
2024-02-15 22:07:50 +01:00
const state = {
modal: ref(null),
checkUpgrade: ref(false),
2024-02-15 22:07:54 +01:00
systemPatchs: reactive([]),
showLog: ref(false),
showLogPatch: ref({}),
hadUpdate: ref(false),
updatePercent: ref(0),
upgradePatch: ref({}),
facAliase: "",
2024-02-15 22:07:50 +01:00
bsModal: {},
}
2024-02-15 22:07:54 +01:00
watchEffect(async () => {
if (checkUpgrade.value) {
2024-02-15 22:07:50 +01:00
let result = await func("/upgrade/checkHelpNet");
if (result.status === "error") {
alertMsg(result.msg, "error");
return;
}
2024-02-15 22:07:54 +01:00
if (!patchSn.value) {
2024-02-15 22:07:50 +01:00
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("<cn>已经是最新版本</cn><en>It is the latest version</en>", "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("<cn>已经是最新版本</cn><en>It is the latest version</en>", "success");
context.emit('update:checkUpgrade', false);
return;
}
state.systemPatchs.splice(0);
state.systemPatchs.push(...result.data);
2024-02-15 22:07:52 +01:00
let hadImpact = false;
2024-02-15 22:07:54 +01:00
for (let i = 0; i < state.systemPatchs.length; i++) {
2024-02-15 22:07:52 +01:00
state.systemPatchs[i].allow = true;
2024-02-15 22:07:54 +01:00
if (!hadImpact) {
2024-02-15 22:07:52 +01:00
const impact = state.systemPatchs[i].impact;
hadImpact = impact === "1";
} else {
state.systemPatchs[i].allow = false;
}
}
2024-02-15 22:07:50 +01:00
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: '<cn>注意</cn><en>Tip</en>',
content: '<cn>设备可能升级过其他固件,如果继续升级,功能可能会被覆盖,是否继续?</cn><en>The device may have been upgraded with custom firmware, and the upgrade function may be overwritten. Do you want to continue?</en>',
buttons: {
ok: {
text: "<cn>继续</cn><en>Continue</en>",
btnClass: 'btn-primary',
keys: ['enter'],
action: () => {
state.bsModal.show();
}
},
cancel: {
text: "<cn>取消</cn><en>Cancel</en>",
2024-02-15 22:07:54 +01:00
action: () => {
2024-02-15 22:07:50 +01:00
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("<cn>无效固件编号</cn><en>Invalid upgrade sn</en>", "error");
context.emit('update:checkUpgrade', false);
return;
}
state.facAliase = result.data[0].aliase;
2024-02-15 22:07:54 +01:00
result = await func("/upgrade/getSystemPatchBySn", {"sn": patchSn.value});
2024-02-15 22:07:50 +01:00
if (result.data.length === 0) {
alertMsg("<cn>无效固件编号</cn><en>Invalid upgrade sn</en>", "error");
context.emit('update:checkUpgrade', false);
return;
}
state.systemPatchs.splice(0);
state.systemPatchs.push(...result.data);
2024-02-15 22:07:52 +01:00
let hadImpact = false;
2024-02-15 22:07:54 +01:00
for (let i = 0; i < state.systemPatchs.length; i++) {
2024-02-15 22:07:52 +01:00
state.systemPatchs[i].allow = true;
2024-02-15 22:07:54 +01:00
if (!hadImpact) {
2024-02-15 22:07:52 +01:00
const impact = state.systemPatchs[i].impact;
hadImpact = impact === "1";
} else {
state.systemPatchs[i].allow = false;
}
}
2024-02-15 22:07:50 +01:00
state.bsModal.show();
}
}
})
2024-02-15 22:07:54 +01:00
const handleVersionLogs = computed(() => {
2024-02-15 22:07:50 +01:00
const regex = /[\r\n\t]/g;
2024-02-15 22:07:54 +01:00
state.showLogPatch.value.description = state.showLogPatch.value.description.replace(regex, "");
return state.showLogPatch.value.description.split(";").filter((item) => {
2024-02-15 22:07:50 +01:00
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 => {
2024-02-15 22:07:54 +01:00
if (state.hadUpdate.value)
2024-02-15 22:07:50 +01:00
return;
state.upgradePatch.value = state.systemPatchs[idx];
const patch = state.systemPatchs[idx];
const chip = patch.chip;
let name = patch.name;
let type = "update";
2024-02-15 22:07:54 +01:00
if (name.indexOf("_sn_") > 0) {
name = name.replace("_sn_", "_" + state.facAliase + "_");
2024-02-15 22:07:50 +01:00
type = "sn";
} else {
2024-02-15 22:07:54 +01:00
name = name.replace("_", "_" + state.facAliase + "_");
2024-02-15 22:07:50 +01:00
type = "update";
}
const params = {
2024-02-15 22:07:54 +01:00
action: "update", name: name,
chip: chip, type: type
2024-02-15 22:07:50 +01:00
}
axios_post('/link/upgrade.php', params)
.then(async data => {
const total = Number(data.size);
state.hadUpdate.value = true;
state.updatePercent.value = 0;
2024-02-15 22:07:54 +01:00
if (total > 0) {
2024-02-15 22:07:50 +01:00
const timerId = setInterval(async function () {
const size = await getUpdateFileSize(name);
2024-02-15 22:07:54 +01:00
state.updatePercent.value = parseInt(size / total * 100);
2024-02-15 22:07:50 +01:00
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";
2024-02-15 22:07:54 +01:00
if (name.indexOf("_sn_") > 0) {
name = name.replace("_sn_", "_" + state.facAliase + "_");
2024-02-15 22:07:50 +01:00
type = "sn";
} else {
2024-02-15 22:07:54 +01:00
name = name.replace("_", "_" + state.facAliase + "_");
2024-02-15 22:07:50 +01:00
type = "update";
}
2024-02-15 22:07:54 +01:00
const url = "http://help.linkpi.cn:5735/upgrade/" + chip + "/" + type + "/" + name;
2024-02-15 22:07:50 +01:00
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);
}
2024-02-15 22:07:54 +01:00
onMounted(() => {
2024-02-15 22:07:50 +01:00
state.bsModal = new bootstrap.Modal(state.modal.value);
2024-02-15 22:07:54 +01:00
state.modal.value.addEventListener('hide.bs.modal', () => {
2024-02-15 22:07:50 +01:00
context.emit('update:modelShow', false);
context.emit('update:checkUpgrade', false);
});
})
2024-02-15 22:07:54 +01:00
return {
...state,
modalFade,
handleVersionLogs,
showPatchVersionLog,
hidePatchVersionLog,
handleUpdatePatch,
handleDownloadPatch
}
2024-02-15 22:07:50 +01:00
}
}
export const customModalComponent = {
template: `<div :class="['modal',{'fade':modalFade}]" tabindex="-1" aria-hidden="true" ref="modal">
<div :class="['modal-dialog modal-dialog-centered',modalSize]">
<div :class="['modal-content',contentClass]">
<div class="modal-header" v-if="hadHeader">
<h5 class="modal-title">{{modalTitle}}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div :class="['modal-body',bodyClass]">
<slot></slot>
</div>
<div class="modal-footer" v-if="hadFooter">
<button v-if="cancelCloseModal" type="button" class="btn btn-secondary" data-bs-dismiss="modal">{{modalCancelBtnName}}</button>
<button v-else type="button" class="btn btn-secondary" @click="cancelBtnClick">{{modalCancelBtnName}}</button>
<button type="button" v-if="hadConfirmBtn" class="btn btn-primary" @click="confirmBtnClick">{{modalConfirmBtnName}}</button>
</div>
</div>
</div>
</div>`,
props: {
modalSize: {
2024-02-15 22:07:54 +01:00
type: String,
default: ""
2024-02-15 22:07:50 +01:00
},
hadHeader: {
type: Boolean,
default: true
},
hadFooter: {
type: Boolean,
default: true
},
modalTitle: {
2024-02-15 22:07:54 +01:00
type: String,
2024-02-15 22:07:50 +01:00
default: "标题"
},
modalShow: {
type: Boolean,
default: false
},
modalFade: {
type: Boolean,
default: true
},
bodyClass: {
type: String,
default: ""
},
contentClass: {
type: String,
default: ""
},
confirmBtnName: {
type: String,
2024-02-15 22:07:54 +01:00
default: "确定"
2024-02-15 22:07:50 +01:00
},
cancelBtnName: {
type: String,
2024-02-15 22:07:54 +01:00
default: "取消"
2024-02-15 22:07:50 +01:00
},
hadConfirmBtn: {
type: Boolean,
default: true
},
2024-02-15 22:07:54 +01:00
cancelCloseModal: {
2024-02-15 22:07:50 +01:00
type: Boolean,
default: true
}
},
2024-02-15 22:07:54 +01:00
setup(props, context) {
2024-02-15 22:07:50 +01:00
2024-02-15 22:07:54 +01:00
const {modalShow, modalFade, modalTitle} = toRefs(props);
2024-02-15 22:07:50 +01:00
const state = {
modal: ref(null),
modalTitle: ref(""),
2024-02-15 22:07:54 +01:00
modalConfirmBtnName: ref("确定"),
modalCancelBtnName: ref("取消"),
2024-02-15 22:07:50 +01:00
show: false,
bsModal: {},
}
2024-02-15 22:07:54 +01:00
watch(modalShow, () => {
if (Object.keys(state.bsModal).length === 0)
return;
2024-02-15 22:07:50 +01:00
state.show = !state.show;
2024-02-15 22:07:54 +01:00
if (state.show)
2024-02-15 22:07:50 +01:00
state.bsModal.show();
else
state.bsModal.hide();
context.emit('modal-visible', state.show);
})
2024-02-15 22:07:54 +01:00
watch(modalTitle, () => {
2024-02-15 22:07:50 +01:00
updateLangText();
})
const initBsModal = () => {
state.bsModal = new bootstrap.Modal(state.modal.value);
2024-02-15 22:07:54 +01:00
if (modalShow.value) {
2024-02-15 22:07:50 +01:00
state.bsModal.show();
state.show = true;
} else {
state.bsModal.hide();
state.show = false;
}
context.emit('modal-visible', state.show);
2024-02-15 22:07:54 +01:00
state.modal.value.addEventListener('hide.bs.modal', () => {
2024-02-15 22:07:50 +01:00
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');
2024-02-15 22:07:54 +01:00
if (props.modalTitle !== undefined) {
const [title1, title2] = props.modalTitle.split("&");
if (lang === "cn" || title2 === undefined)
2024-02-15 22:07:50 +01:00
state.modalTitle.value = title1;
else
state.modalTitle.value = title2;
}
2024-02-15 22:07:54 +01:00
if (props.confirmBtnName !== undefined) {
const [name1, name2] = props.confirmBtnName.split("&");
if (lang === "cn" || name2 === undefined)
2024-02-15 22:07:50 +01:00
state.modalConfirmBtnName.value = name1;
else
state.modalConfirmBtnName.value = name2;
}
2024-02-15 22:07:54 +01:00
if (props.cancelBtnName !== undefined) {
const [name1, name2] = props.cancelBtnName.split("&");
if (lang === "cn" || name2 === undefined)
2024-02-15 22:07:50 +01:00
state.modalCancelBtnName.value = name1;
else
state.modalCancelBtnName.value = name2;
}
}
2024-02-15 22:07:54 +01:00
onMounted(() => {
2024-02-15 22:07:50 +01:00
const html = document.querySelector('html');
2024-02-15 22:07:54 +01:00
html.addEventListener("loaded", () => {
2024-02-15 22:07:50 +01:00
updateLangText();
initBsModal();
})
const observer = new mutationObserver(() => {
updateLangText();
});
const config = {
attributes: true,
attributeFilter: ["data-bs-language"],
subtree: false
};
observer.observe(html, config);
})
2024-02-15 22:07:54 +01:00
return {...state, modalFade, confirmBtnClick, cancelBtnClick}
2024-02-15 22:07:50 +01:00
}
}
export const loadingButtonComponent = {
template: `<button type="button" :class="customClass" @click="onButtonClick">
<span v-if="hadLoading" class="spinner-border spinner-border-sm"></span>
<span v-else >
<slot></slot>
</span>
</button>`,
2024-02-15 22:07:52 +01:00
props: {
customClass: {
type: String,
default: ""
},
hadLoading: {
type: Boolean,
default: false
}
},
2024-02-15 22:07:54 +01:00
setup(props, context) {
2024-02-15 22:07:50 +01:00
const onButtonClick = () => {
2024-02-15 22:07:54 +01:00
context.emit("button-click", "click")
2024-02-15 22:07:50 +01:00
}
2024-02-15 22:07:54 +01:00
return {onButtonClick}
2024-02-15 22:07:50 +01:00
}
}
export const ptzDirectComponent = {
template: `<div class="row">
<div class="col-lg-12">
<div class="row">
<div class="col-lg-6 text-center">
<cn>云台控制</cn>
<en>PTZ</en>
</div>
<div class="col-lg-6 text-center">
<cn>预置位</cn>
<en>Preset</en>
</div>
</div>
</div>
<div class="col-lg-12 mt-3">
<div class="row">
<div class="col-lg-6">
<div class="row">
<div class="col-lg-12 text-center">
<button type="button" @mousedown="handlePtzMove('left-up')" @mouseup="handlePtzMove('move-stop')" :class="['btn btn-primary',arrowClass,{'lp-visibility-hide':!sticks.includes('left-up')}]">
<i class="fa-solid fa-circle-arrow-up" style="transform: rotate(-45deg);-o-transform: rotate(-45deg);-webkit-transform: rotate(-45deg);-moz-transform: rotate(-45deg);"></i>
</button>
<button type="button" @mousedown="handlePtzMove('up')" @mouseup="handlePtzMove('move-stop')" :class="['btn btn-primary',arrowClass,{'lp-visibility-hide':!sticks.includes('up')}]" :style="{'margin':'0px '+gop+'px'}">
<i class="fa-solid fa-circle-arrow-up"></i>
</button>
<button type="button" @mousedown="handlePtzMove('right-up')" @mouseup="handlePtzMove('move-stop')" :class="['btn btn-primary',arrowClass,{'lp-visibility-hide':!sticks.includes('right-up')}]">
<i class="fa-solid fa-circle-arrow-up" style="transform: rotate(45deg);-o-transform: rotate(45deg);-webkit-transform: rotate(45deg);-moz-transform: rotate(45deg);"></i>
</button>
</div>
<div class="col-lg-12 text-center" :style="{'marginTop':gop+'px'}">
<button type="button" @mousedown="handlePtzMove('left')" @mouseup="handlePtzMove('move-stop')" :class="['btn btn-primary',arrowClass,{'lp-visibility-hide':!sticks.includes('left')}]">
<i class="fa-solid fa-circle-arrow-left"></i>
</button>
<button type="button" @mouseup="handlePtzMove('home')" :class="['btn btn-primary',homeClass,{'lp-visibility-hide':!sticks.includes('home')}]" :style="{'margin':'0px '+gop+'px'}">
<i class="fa-solid fa-house"></i>
</button>
<button type="button" @mousedown="handlePtzMove('right')" @mouseup="handlePtzMove('move-stop')" :class="['btn btn-primary',arrowClass,{'lp-visibility-hide':!sticks.includes('right')}]">
<i class="fa-solid fa-circle-arrow-right"></i>
</button>
</div>
<div class="col-lg-12 text-center" :style="{'marginTop':gop+'px'}">
<button type="button" @mousedown="handlePtzMove('left-down')" @mouseup="handlePtzMove('move-stop')" :class="['btn btn-primary',arrowClass,{'lp-visibility-hide':!sticks.includes('left-down')}]">
<i class="fa-solid fa-circle-arrow-up" style="transform: rotate(-135deg);-o-transform: rotate(-135deg);-webkit-transform: rotate(-135deg);-moz-transform: rotate(-135deg);"></i>
</button>
<button type="button" @mousedown="handlePtzMove('down')" @mouseup="handlePtzMove('move-stop')" :class="['btn btn-primary',arrowClass,{'lp-visibility-hide':!sticks.includes('down')}]" :style="{'margin':'0px '+gop+'px'}">
<i class="fa-solid fa-circle-arrow-down"></i>
</button>
<button type="button" @mousedown="handlePtzMove('right-down')" @mouseup="handlePtzMove('move-stop')" :class="['btn btn-primary',arrowClass,{'lp-visibility-hide':!sticks.includes('right-down')}]">
<i class="fa-solid fa-circle-arrow-up" style="transform: rotate(135deg);-o-transform: rotate(135deg);-webkit-transform: rotate(135deg);-moz-transform: rotate(130deg);"></i>
</button>
</div>
</div>
<div class="row mt-4">
<div class="col-lg-3 text-center">
<cn>焦距</cn>
<en>Zoom</en>
</div>
<div class="col-lg-7 pt-2">
<noui-slider v-model="zoom" :min="zoomMin" :max="zoomMax" :step="zoomStep" :fix="zoomFix" @slide-end="onTouchSlideEnd"></noui-slider>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="row">
<div class="col-lg-12 text-center">
<button type="button" @click="updatePreset(1)" :class="['btn',{'btn-primary':presetVal===1},{'btn-default':presetVal!==1},arrowClass]">
1
</button>
<button type="button" @click="updatePreset(2)" :class="['btn',{'btn-primary':presetVal===2},{'btn-default':presetVal!==2},arrowClass]" :style="{'margin':'0px '+gop+'px'}">
2
</button>
<button type="button" @click="updatePreset(3)" :class="['btn',{'btn-primary':presetVal===3},{'btn-default':presetVal!==3},arrowClass]">
3
</button>
</div>
<div class="col-lg-12 text-center" :style="{'marginTop':gop+'px'}">
<button type="button" @click="updatePreset(4)" :class="['btn',{'btn-primary':presetVal===4},{'btn-default':presetVal!==4},arrowClass]">
4
</button>
<button type="button" @click="updatePreset(5)" :class="['btn',{'btn-primary':presetVal===5},{'btn-default':presetVal!==5},arrowClass]" :style="{'margin':'0px '+gop+'px'}">
5
</button>
<button type="button" @click="updatePreset(6)" :class="['btn',{'btn-primary':presetVal===6},{'btn-default':presetVal!==6},arrowClass]">
6
</button>
</div>
<div class="col-lg-12 text-center" :style="{'marginTop':gop+'px'}">
<button type="button" @click="updatePreset(7)" :class="['btn',{'btn-primary':presetVal===7},{'btn-default':presetVal!==7},arrowClass]">
7
</button>
<button type="button" @click="updatePreset(8)" :class="['btn',{'btn-primary':presetVal===8},{'btn-default':presetVal!==8},arrowClass]" :style="{'margin':'0px '+gop+'px'}">
8
</button>
<button type="button" @click="updatePreset(9)" :class="['btn',{'btn-primary':presetVal===9},{'btn-default':presetVal!==9},arrowClass]">
9
</button>
</div>
</div>
<div class="row mt-3">
<div class="row-lg-12 text-center">
<button type="button" class="btn btn-primary border-3 px-3 me-1" @click="handleCallPreset">
<cn>调用</cn>
<en>Get</en>
</button>
<button type="button" class="btn btn-primary border-3 px-3" @click="handleSetPreset">
<cn>设置</cn>
<en>Set</en>
</button>
</div>
</div>
</div>
</div>
</div>
</div>`,
props: {
arrowClass: {
type: String,
default: "",
},
homeClass: {
type: String,
default: "",
},
gop: {
type: Number,
default: 5
},
sticks: {
type: Array,
2024-02-15 22:07:54 +01:00
default: ['left', 'left-up', 'up', 'right-up', 'right', 'right-down', 'down', 'left-down', 'home']
2024-02-15 22:07:50 +01:00
},
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
}
},
2024-02-15 22:07:54 +01:00
components: {
2024-02-15 22:07:50 +01:00
"noui-slider": nouiSliderComponent
},
2024-02-15 22:07:54 +01:00
setup(props, context) {
2024-02-15 22:07:50 +01:00
const state = {
zoom: ref(0),
presetVal: ref(0)
}
2024-02-15 22:07:54 +01:00
watch(() => props.zoomVal, (newValue, oldValue) => {
2024-02-15 22:07:50 +01:00
state.zoom.value = newValue;
})
const handlePtzMove = type => {
2024-02-15 22:07:54 +01:00
context.emit("ptz-move", type)
2024-02-15 22:07:50 +01:00
}
const updatePreset = val => {
state.presetVal.value = val;
}
const onTouchSlideEnd = val => {
context.emit('zoom-change', val);
}
const handleCallPreset = () => {
2024-02-15 22:07:54 +01:00
context.emit('call-preset', state.presetVal.value);
2024-02-15 22:07:50 +01:00
}
const handleSetPreset = () => {
2024-02-15 22:07:54 +01:00
context.emit('set-preset', state.presetVal.value);
2024-02-15 22:07:50 +01:00
}
2024-02-15 22:07:54 +01:00
return {...state, handlePtzMove, onTouchSlideEnd, updatePreset, handleCallPreset, handleSetPreset}
2024-02-15 22:07:50 +01:00
}
}
export const usbOptionComponent = {
2024-02-15 22:07:54 +01:00
template: `<a :class="['nav-link lp-usb-ctx',{'active':hadMountDisk}]" data-bs-toggle="dropdown">
2024-02-15 22:07:50 +01:00
<div class="lp-usb-drive">
<div class="lp-usb-body"></div>
<div class="lp-usb-metal"></div>
<div class="lp-usb-hole"></div>
</div>
</a>
<div class="dropdown">
<ul class="dropdown-menu dropdown-menu-end">
<li v-if="hadMountDisk">
<a class="dropdown-item" href="javascript:;" @click="unInstallDisk">
<span class="material-symbols-outlined me-2">
<i class="fa-solid fa-arrow-up-from-bracket me-2"></i>
<cn>弹出设备</cn>
<en>Uninstall</en>
</span>
</a>
</li>
<li v-if="hadMountDisk"><hr></li>
<li v-if="hadMountDisk && Object.keys(diskConf).length > 0 && diskConf.used==='local' && diskConf.local.device!=='/dev/mmcblk0p6'">
<a class="dropdown-item" href="javascript:;" @click="formatDisk">
<span class="material-symbols-outlined me-2">
<i class="fa-solid fa-circle-nodes me-2"></i>
<cn>格式化</cn>
<en>Format Disk</en>
</span>
</a>
</li>
<li v-if="hadMountDisk && Object.keys(diskConf).length > 0 && diskConf.used==='local' && diskConf.local.device!=='/dev/mmcblk0p6'"><hr></li>
<li>
<a class="dropdown-item" href="javascript:;" @click="turnMountDisk">
<span class="material-symbols-outlined me-2">
<i class="fa-solid fa-right-left me-2"></i>
<cn>切换挂载</cn>
<en>Change Disk</en>
</span>
</a>
</li>
<li v-if="hadMountDisk"><hr></li>
<li v-if="hadMountDisk">
<a class="dropdown-item text-center" href="javascript:;">
<span class="material-symbols-outlined me-2">
<cn>已用</cn><en>Used</en>
{{hadMountInfo.used + ' / ' + hadMountInfo.total}}
</span>
</a>
</li>
</ul>
</div>`,
2024-02-15 22:07:54 +01:00
setup(props, context) {
2024-02-15 22:07:50 +01:00
const hadMountInfo = reactive({});
const hadMountDisk = ref(false);
2024-02-15 22:07:54 +01:00
const {diskConf, handleDiskConf, updateDiskConf} = useDiskConf();
2024-02-15 22:07:50 +01:00
const unInstallDisk = () => {
confirm({
title: '<cn>卸载磁盘</cn><en>UnInstall Disk</en>',
content: '<cn>是否卸载磁盘,请确保没有处于录制状态</cn><en>Whether to uninstall the disk, please make sure it is not in the recording state</en>',
buttons: {
ok: {
text: "<cn>卸载</cn><en>Confirm</en>",
btnClass: 'btn-primary',
action: () => {
func("/system/umountDisk").then(res => {
2024-02-15 22:07:54 +01:00
alertMsg(res.msg, res.status);
2024-02-15 22:07:50 +01:00
})
}
},
cancel: {
text: "<cn>取消</cn><en>Cancel</en>",
2024-02-15 22:07:54 +01:00
action: () => {
}
2024-02-15 22:07:50 +01:00
}
}
});
}
const formatDisk = () => {
confirm({
title: '<cn>格式化磁盘</cn><en>Formatted Disk</en>',
content: `<div class="row">
<div class="col-lg-11">
<div class="row mt-2">
<div class="col-lg-3 lp-align-center">
<label>
<cn>磁盘格式</cn>
<en>Format</en>
</label>
</div>
<div class="col-lg-9">
<select class="form-select" id="diskFormat">
<option value="ext4">EXT4</option>
<option value="fat32">FAT32</option>
</select>
</div>
</div>
<div class="row mt-2">
<div class="col-lg-3 lp-align-center">
<label>
<cn>登录密码</cn>
<en>Password</en>
</label>
</div>
<div class="col-lg-9">
<input class="form-control" type="password" id="formatPasswd" autocomplete="off">
</div>
</div>
<div class="row mt-4">
<div class="col-lg-12 p-0">
<label class="ms-3">
<cn>Tip: 格式化将清空磁盘数据且不可逆转请谨慎操作</cn>
<en>Tip: Formatting will erase disk data and is irreversible.</en>
</label>
</div>
</div>
</div>
</div>`,
buttons: {
ok: {
text: "<cn>格式化</cn><en>Format</en>",
btnClass: 'btn-primary',
action: () => {
const formatPasswd = document.querySelector("#formatPasswd").value;
2024-02-15 22:07:54 +01:00
func("/system/formatReady", {"psd": formatPasswd}).then(res => {
return new Promise((resolve, reject) => {
if (res.status === "error") {
alertMsg(res.msg, res.status);
2024-02-15 22:07:50 +01:00
reject();
return;
}
resolve();
})
2024-02-15 22:07:54 +01:00
}).then(() => {
2024-02-15 22:07:50 +01:00
const diskFormat = document.querySelector("#diskFormat").value;
2024-02-15 22:07:54 +01:00
const notify = alertMsg("<cn>正在格式化,请勿关闭此页面</cn><en>Do not close this page while formatting</en>", "success", 99999999);
func("/system/formatDisk", {"format": diskFormat});
let interval = setInterval(() => {
2024-02-15 22:07:50 +01:00
func("/system/checkFormatProgress").then(res => {
2024-02-15 22:07:54 +01:00
if (res.data === 0) {
2024-02-15 22:07:50 +01:00
clearInterval(interval);
notify.remove();
2024-02-15 22:07:54 +01:00
setTimeout(() => alertMsg(res.msg, res.status), 600);
2024-02-15 22:07:50 +01:00
}
})
2024-02-15 22:07:54 +01:00
}, 5000);
2024-02-15 22:07:50 +01:00
})
}
},
cancel: {
text: "<cn>取消</cn><en>Cancel</en>",
2024-02-15 22:07:54 +01:00
action: () => {
}
2024-02-15 22:07:50 +01:00
}
}
});
}
const checkMountDisk = () => {
func("/system/getMountDiskSpace").then(res => {
2024-02-15 22:07:54 +01:00
Object.assign(hadMountInfo, res.data);
2024-02-15 22:07:50 +01:00
hadMountDisk.value = (res.status === "success");
})
2024-02-15 22:07:54 +01:00
setTimeout(checkMountDisk, 1000);
2024-02-15 22:07:50 +01:00
}
const turnMountDisk = () => {
const html = document.querySelector("html");
const lang = html.getAttribute("data-bs-language");
const jc = confirm({
title: '<cn>磁盘挂载</cn><en>Mount Disk</en>',
content: `<div class="row">
<div class="col-lg-12">
<div class="row mt-3">
<div class="col-lg-3 offset-lg-1 lp-align-center">
<label>
<cn>类型</cn>
<en>Type</en>
</label>
</div>
<div class="col-lg-7">
<select class="form-select" id="mount_device">
<option value="shared">${lang === "cn" ? "网络磁盘" : "net disk"}</option>
<option value="local">${lang === "cn" ? "移动磁盘" : "usb disk"}</option>
</select>
</div>
</div>
<div class="row mt-3 local-device">
<div class="col-lg-3 offset-lg-1 lp-align-center">
<label>
<cn>设备</cn>
<en>Device</en>
</label>
</div>
<div class="col-lg-7">
<select class="form-select" id="local_devices"></select>
</div>
</div>
<div class="row mt-3 share-device">
<div class="col-lg-3 offset-lg-1 lp-align-center">
<label>
<cn>协议</cn>
<en>Protocol</en>
</label>
</div>
<div class="col-lg-7">
<select class="form-select" id="shared_protocol">
<option value="cifs">${lang === "cn" ? "cifs (windows共享目录)" : "cifs (windows shared directory)"}</option>
<option value="nfs">nfs</option>
</select>
</div>
</div>
<div class="row mt-3 cifs-auth share-device">
<div class="col-lg-3 offset-lg-1 lp-align-center">
<label>
<cn>用户名<small style="color: gray;font-size: 11px;">(选填)</small></cn>
<en>Username</en>
</label>
</div>
<div class="col-lg-7">
<input class="form-control" id="shared_uname">
</div>
</div>
<div class="row mt-3 cifs-auth share-device">
<div class="col-lg-3 offset-lg-1 lp-align-center">
<label>
<cn>密码<small style="color: gray;font-size: 11px;">(选填)</small></cn>
<en>Password</en>
</label>
</div>
<div class="col-lg-7">
<input class="form-control" id="shared_passwd">
</div>
</div>
<div class="row mt-3 share-device">
<div class="col-lg-3 offset-lg-1 lp-align-center">
<label>
<cn>IP地址</cn>
<en>IP Address</en>
</label>
</div>
<div class="col-lg-7">
<input class="form-control" id="shared_ip">
</div>
</div>
<div class="row mt-3 share-device">
<div class="col-lg-3 offset-lg-1 lp-align-center">
<label>
<cn>挂载路径</cn>
<en>Mount Path</en>
</label>
</div>
<div class="col-lg-7">
<input class="form-control" id="shared_path">
</div>
</div>
<div class="row" style="padding-top: 30px;padding-left: 30px;color: gray">
<label class="col-lg-12">
<cn>Tip: 更换挂载设备时请确保没有处于录制状态</cn>
<en>Tip: Make sure that you are not recording when you change the mounted device</en>
</label>
</div>
</div>
</div>`,
buttons: {
ok: {
text: "<cn>挂载</cn><en>Mount</en>",
btnClass: 'btn-primary',
action: () => {
updateDiskConf({
enable: true,
used: document.querySelector('#mount_device').value,
shared: {
2024-02-15 22:07:54 +01:00
ip: document.querySelector('#shared_ip').value,
2024-02-15 22:07:50 +01:00
type: document.querySelector('#shared_protocol').value,
path: document.querySelector('#shared_path').value,
2024-02-15 22:07:54 +01:00
auth: {
2024-02-15 22:07:50 +01:00
uname: document.querySelector('#shared_uname').value,
passwd: document.querySelector('#shared_passwd').value,
}
},
local: {
2024-02-15 22:07:54 +01:00
device: document.querySelector('#local_devices').value
2024-02-15 22:07:50 +01:00
}
2024-02-15 22:07:54 +01:00
}).then(async () => {
2024-02-15 22:07:50 +01:00
handleDiskConf();
2024-02-15 22:07:54 +01:00
alertMsg("<cn>磁盘检测中,请稍后...</cn><en>Disk checking, please wait...</en>", "success");
2024-02-15 22:07:50 +01:00
const result = await func("/system/mountDisk");
2024-02-15 22:07:54 +01:00
if (result.status === "success")
2024-02-15 22:07:50 +01:00
jc.close();
2024-02-15 22:07:54 +01:00
setTimeout(() => alertMsg(result.msg, result.status), 600);
2024-02-15 22:07:50 +01:00
});
return false;
}
},
cancel: {
text: "<cn>取消</cn><en>Cancel</en>",
2024-02-15 22:07:54 +01:00
action: () => {
}
2024-02-15 22:07:50 +01:00
}
},
2024-02-15 22:07:54 +01:00
onOpenBefore: () => {
const display = (type, protocol) => {
2024-02-15 22:07:50 +01:00
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');
2024-02-15 22:07:54 +01:00
if (type === "shared") {
2024-02-15 22:07:50 +01:00
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');
2024-02-15 22:07:54 +01:00
if (protocol === 'cifs')
2024-02-15 22:07:50 +01:00
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;
2024-02-15 22:07:54 +01:00
if (item.name === "/dev/mmcblk0p6") {
if (lang === "cn")
2024-02-15 22:07:50 +01:00
item.name = "内部存储";
else
item.name = "device storage";
}
2024-02-15 22:07:54 +01:00
option.text = item.name + "( " + item.size + " )";
2024-02-15 22:07:50 +01:00
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;
2024-02-15 22:07:54 +01:00
display(diskConf.used, diskConf.shared.type);
2024-02-15 22:07:50 +01:00
document.querySelector('#mount_device').addEventListener('change', () => {
const type = document.querySelector('#mount_device').value;
2024-02-15 22:07:54 +01:00
display(type, "cifs");
2024-02-15 22:07:50 +01:00
});
document.querySelector('#shared_protocol').addEventListener('change', () => {
const protocol = document.querySelector('#shared_protocol').value;
2024-02-15 22:07:54 +01:00
display("shared", protocol);
2024-02-15 22:07:50 +01:00
});
}
});
}
onMounted(checkMountDisk);
2024-02-15 22:07:54 +01:00
return {hadMountInfo, hadMountDisk, diskConf, unInstallDisk, formatDisk, turnMountDisk}
2024-02-15 22:07:50 +01:00
}
}