linkpi_firmware_history/rootfs/link/webflex/sys.php

1399 lines
79 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php include ("./link/session.php") ?>
<!doctype html>
<html lang="uft-8">
<head>
<?php include ("./public/head.inc") ?>
<link href="assets/plugins/fileinput/css/fileinput.min.css" rel="stylesheet" >
</head>
<body>
<?php include ("./public/menu.inc") ?>
<div data-simplebar class="mb-4">
<main class="page-content sys" id="app" v-cloak>
<div class="row">
<div class="col-lg-6">
<ul class="nav nav-tabs nav-primary" role="tablist">
<li class="nav-item" role="presentation" v-if="Object.keys(netAdapter).length > 0 && Object.keys(netManagerConf).length > 0" v-for="(item,index) in Object.values(netAdapter)" :key="index">
<a v-if="netManagerConf.interface.hasOwnProperty(item.dev)" :class="['nav-link',{'active':index===0}]" data-bs-toggle="tab" :href="'#tab'+(index+1)" role="tab" aria-selected="true">
<div v-if="item.type === 'lan'" class="d-flex align-items-center">
<div class="tab-icon"><i :class="['fa-solid me-1',{'fa-code-merge':index%2===1},{'fa-code-fork':index%2===0}]"></i></i></div>
<div class="tab-title">
<cn>网口</cn>
<en>LAN</en>
<span v-if="index > 0">{{index+1}}</span>
</div>
</div>
<div v-if="item.type === 'wifi'" class="d-flex align-items-center">
<wifi-flag :icon="'wifi-'+(item.rssi > 3 ? 4 : (item.rssi < 3 ? (item.rssi === 0 ? 1 : 2) : 3))" :width="20" :height="20" :stroke="'#cccccc'" :color="'#777777'" :stroke-width="2.3"></wifi-flag>
<div class="tab-title">
<cn>无线网</cn>
<en>WIFI</en>
</div>
</div>
<div v-if="item.type === 'dongle'" class="d-flex align-items-center">
<antenan-flag :icon="'antenan-'+(item.rssi > 3 ? 4 : (item.rssi < 3 ? (item.rssi === 0 ? 0 : 2) : 3))" :width="20" :height="20" :stroke="'#cccccc'" :color="'#777777'" :stroke-width="2.3"></antenan-flag>
<div class="tab-title">
<cn>移动网络</cn>
<en>Cellular network</en>
</div>
</div>
</a>
</li>
<li v-if="Object.keys(hardwareConf).length > 0 && hardwareConf.function.wifi && !Object.values(netAdapter).some(item => item.type === 'wifi')" class="nav-item lp-cursor-pointer" ref="wifiHandler">
<a class="nav-link">
<div class="d-flex align-items-center">
<wifi-flag icon="wifi-off" :width="20" :height="20" :stroke="'#999999'" :stroke-width="2.3"></wifi-flag>
<div class="tab-title">
<cn>无线网</cn>
<en>WIFI</en>
</div>
</div>
</a>
</li>
<li v-if="!Object.values(netAdapter).some(item => item.type === 'dongle')" class="nav-item lp-cursor-pointer" ref="antenanHandler">
<a class="nav-link">
<div class="d-flex align-items-center">
<antenan-flag icon="antenan-off" :width="20" :height="20" :stroke="'#999999'" :stroke-width="2.3"></antenan-flag>
<div class="tab-title">
<cn>移动网络</cn>
<en>Cellular network</en>
</div>
</div>
</a>
</li>
</ul>
<div class="tab-content py-3 pe-2 ps-2">
<div v-if="Object.keys(netAdapter).length > 0 && Object.keys(netManagerConf).length > 0" v-for="(item,index) in Object.values(netAdapter)" :class="['tab-pane fade',{'show active':index===0}]" :key="index" :id="'tab'+(index+1)" role="tabpanel">
<div v-if="netManagerConf.interface.hasOwnProperty(item.dev) && item.type === 'lan'">
<div v-if="Object.keys(hardwareConf).length > 0 && hardwareConf.function.dhcp" class="row mt-3">
<div class="col-lg-2 offset-lg-1 lp-align-center">
<label>
DHCP
</label>
</div>
<div class="col-lg-5">
<bs-switch v-model="netManagerConf.interface[item.dev].dhcp" :size="'normal'"></bs-switch>
</div>
</div>
<div class="row mt-3">
<div class="col-lg-2 offset-lg-1 lp-align-center">
<label>
IP
</label>
</div>
<div class="col-lg-6">
<input class="form-control" v-model.trim.lazy="netManagerConf.interface[item.dev].ip">
</div>
</div>
<div class="row mt-3">
<div class="col-lg-2 offset-lg-1 lp-align-center">
<label>
<cn>掩码</cn>
<en>Mask</en>
</label>
</div>
<div class="col-lg-6">
<input class="form-control" v-model.trim.lazy="netManagerConf.interface[item.dev].mask">
</div>
</div>
<div class="row mt-3">
<div class="col-lg-2 offset-lg-1 lp-align-center">
<label>
<cn>网关</cn>
<en>Gateway</en>
</label>
</div>
<div class="col-lg-6">
<input class="form-control" v-model.trim.lazy="netManagerConf.interface[item.dev].gw">
</div>
</div>
<div class="row mt-3">
<div class="col-lg-2 offset-lg-1 lp-align-center">
<label>
DNS
</label>
</div>
<div class="col-lg-6">
<input class="form-control" v-model.trim.lazy="netManagerConf.interface[item.dev].dns">
</div>
</div>
<div class="row mt-3">
<div class="col-lg-2 offset-lg-1 lp-align-center">
<label>
MAC
</label>
</div>
<div class="col-lg-6">
<div class="input-group">
<input class="form-control" type="text" :disabled="macLock" v-model.trim.lazy="netManagerConf.interface[item.dev].mac">
<span class="input-group-text input-group-addon lp-cursor-pointer" @click="macLock=!macLock"><i :class="['fa-solid',{'fa-lock':macLock},{'fa-unlock':!macLock}]"></i></span>
</div>
</div>
</div>
<div class="row mt-4">
<div class="col-lg-12 text-center">
<button type="button" class="btn border-3 btn-primary px-4" @click="updateDefNetwork(item.dev)"><cn>保存</cn><en>Save</en></button>
</div>
</div>
</div>
<div v-if="Object.keys(hardwareConf).length > 0 && hardwareConf.function.wifi && netManagerConf.interface.hasOwnProperty(item.dev) && item.type === 'wifi'">
<div class="row mt-4">
<div class="col-lg-6 border-right">
<div class="row mt-4">
<div class="col-lg-3 lp-align-center">
<label>
<cn>启用</cn>
<en>Enable</en>
</label>
</div>
<div class="col-lg-8">
<bs-switch v-model="netManagerConf.interface[item.dev].enable" :size="'normal'" @switch-change="enableWifi"></bs-switch>
</div>
</div>
<div class="row mt-3">
<div class="col-lg-3 lp-align-center">
<label>
WIFI
</label>
</div>
<div class="col-lg-8">
<div class="input-group">
<select class="form-select" v-model="wifiConnectId">
<option v-for="(it,idx) in wifiList" :key="idx+10" :value="it.ssid">{{it.ssid}}</option>
</select>
<span class="input-group-text input-group-addon lp-cursor-pointer" @click="refreshWifi"><i :class="['fa-solid fa-arrows-rotate',{'spin':wifiRefresh}]"></i></span>
</div>
</div>
</div>
<div class="row mt-3">
<div class="col-lg-3 lp-align-center">
<label>
<cn>密码</cn>
<en>Password</en>
</label>
</div>
<div class="col-lg-8">
<div class="input-group">
<input class="form-control" :type="!showPasswd.wifipwd ? 'password' : 'text'" v-model.trim.lazy="wifiPassword">
<span class="input-group-text input-group-addon lp-cursor-pointer" @click="showPasswd.wifipwd = !showPasswd.wifipwd"><i :class="['fa-regular',{'fa-eye-slash':!showPasswd.wifipwd},{'fa-eye':showPasswd.wifipwd}]"></i></span>
</div>
</div>
</div>
<div class="row mt-4">
<div class="col-lg-3 lp-align-center">
<label>
<cn>状态</cn>
<en>Status</en>
</label>
</div>
<div class="col-lg-8">
<label v-if="!netManagerConf.interface[item.dev].enable">
<cn>未启用</cn><en>Disabled</en>
</label>
<label v-else-if="(!item.ssid && wifiPassword) || (!item.linkup && item.ssid)">
<cn class="pointLoading">连接中</cn><en class="pointLoading">connecting</en>
</label>
<label v-else-if="item.linkup && item.ssid">
<cn>已连接</cn><en>connected </en>
{{item.ssid}}
</label>
<label v-else>
<cn>未连接</cn><en>not connected</en>
</label>
</div>
</div>
<div class="row mt-4">
<div class="col-lg-12 text-center">
<button type="button" class="btn border-3 btn-primary px-4 me-2" @click="connectWifi"><cn>连接</cn><en>Connect</en></button>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="row mt-4">
<div class="col-lg-3 lp-align-center">
<label>
DHCP
</label>
</div>
<div class="col-lg-8">
<bs-switch v-model="netManagerConf.interface[item.dev].dhcp" :size="'normal'"></bs-switch>
</div>
</div>
<div class="row mt-3">
<div class="col-lg-3 lp-align-center">
<label>
IP
</label>
</div>
<div class="col-lg-8">
<input class="form-control" v-model.trim.lazy="netManagerConf.interface[item.dev].ip">
</div>
</div>
<div class="row mt-3">
<div class="col-lg-3 lp-align-center">
<label>
<cn>掩码</cn>
<en>Mask</en>
</label>
</div>
<div class="col-lg-8">
<input class="form-control" v-model.trim.lazy="netManagerConf.interface[item.dev].mask">
</div>
</div>
<div class="row mt-3">
<div class="col-lg-3 lp-align-center">
<label>
<cn>网关</cn>
<en>Gateway</en>
</label>
</div>
<div class="col-lg-8">
<input class="form-control" v-model.trim.lazy="netManagerConf.interface[item.dev].gw">
</div>
</div>
<div class="row mt-3">
<div class="col-lg-3 lp-align-center">
<label>
DNS
</label>
</div>
<div class="col-lg-8">
<input class="form-control" v-model.trim.lazy="netManagerConf.interface[item.dev].dns">
</div>
</div>
<div class="row mt-4 mb-4">
<div class="col-lg-12 text-center">
<button type="button" class="btn border-3 btn-primary px-4" @click="updateNetManagerConf"><cn>保存</cn><en>Save</en></button>
</div>
</div>
</div>
</div>
</div>
<div v-if="item.type === 'dongle'">
<div class="row mt-3">
<div class="col-lg-2 offset-lg-1 lp-align-center">
<label>
<cn>运营商</cn>
<en>Operator</en>
</label>
</div>
<div class="col-lg-6">
<div v-if="!netAdapter[item.dev].oper" v-html="`<cn class='pointLoading'>检测中</cn><en class='pointLoading'>Detecting...</en>`"></div>
<input v-else class="form-control" v-model.trim.lazy="netAdapter[item.dev].oper" readonly>
</div>
</div>
<div class="row mt-3">
<div class="col-lg-2 offset-lg-1 lp-align-center">
<label>
<cn>服务</cn>
<en>Service</en>
</label>
</div>
<div class="col-lg-6">
<input class="form-control" v-model.trim.lazy="netAdapter[item.dev].service" readonly>
</div>
</div>
<div class="row mt-3">
<div class="col-lg-2 offset-lg-1 lp-align-center">
<label>
<cn>上行</cn>
<en>Service</en>
</label>
</div>
<div class="col-lg-6">
<input class="form-control" :value="formatNetSpeed(netAdapter[item.dev].tx)" readonly>
</div>
</div>
<div class="row mt-3">
<div class="col-lg-2 offset-lg-1 lp-align-center">
<label>
<cn>下行</cn>
<en>Service</en>
</label>
</div>
<div class="col-lg-6">
<input class="form-control" :value="formatNetSpeed(netAdapter[item.dev].rx)" readonly>
</div>
</div>
<div class="row mt-3">
<div class="col-lg-2 offset-lg-1 lp-align-center">
<label>
IP
</label>
</div>
<div class="col-lg-6">
<input class="form-control" v-model.trim.lazy="netAdapter[item.dev].ip" readonly>
</div>
</div>
<div class="row mt-3">
<div class="col-lg-2 offset-lg-1 lp-align-center">
<label>
<cn>掩码</cn>
<en>Mask</en>
</label>
</div>
<div class="col-lg-6">
<input class="form-control" v-model.trim.lazy="netAdapter[item.dev].mask" readonly>
</div>
</div>
<div class="row mt-3 mb-2">
<div class="col-lg-2 offset-lg-1 lp-align-center">
<label>
<cn>网关</cn>
<en>Gateway</en>
</label>
</div>
<div class="col-lg-6">
<input class="form-control" v-model.trim.lazy="netAdapter[item.dev].gw" readonly>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="row">
<div class="col-lg-12">
<div class="card">
<div class="card-header bg-transparent">
<div class="p-2 mb-0 d-flex align-items-end">
<cn>密码设置</cn>
<en>Password</en>
</div>
</div>
<div class="card-body">
<div class="row">
<div class="row">
<div class="col-lg-2 offset-lg-1 lp-align-center">
<label>
<cn>旧密码</cn>
<en>Current</en>
</label>
</div>
<div class="col-lg-6">
<div class="input-group">
<input class="form-control" :type="!showPasswd.oldpwd ? 'password' : 'text'" v-model.trim.lazy="userPasswd.oldpwd">
<span class="input-group-text input-group-addon lp-cursor-pointer" @click="showPasswd.oldpwd = !showPasswd.oldpwd"><i :class="['fa-regular',{'fa-eye-slash':!showPasswd.oldpwd},{'fa-eye':showPasswd.oldpwd}]"></i></span>
</div>
</div>
</div>
<div class="row mt-3">
<div class="col-lg-2 offset-lg-1 lp-align-center">
<label>
<label>
<cn>新密码</cn>
<en>New</en>
</label>
</label>
</div>
<div class="col-lg-6">
<div class="input-group">
<input class="form-control" :type="!showPasswd.newpwd ? 'password' : 'text'" v-model.trim.lazy="userPasswd.newpwd">
<span class="input-group-text input-group-addon lp-cursor-pointer" @click="showPasswd.newpwd = !showPasswd.newpwd"><i :class="['fa-regular',{'fa-eye-slash':!showPasswd.newpwd},{'fa-eye':showPasswd.newpwd}]"></i></span>
</div>
</div>
</div>
<div class="row mt-3">
<div class="col-lg-2 offset-lg-1 lp-align-center">
<label>
<cn>确认密码</cn>
<en>Confirm</en>
</label>
</div>
<div class="col-lg-6">
<div class="input-group">
<input class="form-control" :type="!showPasswd.confirm ? 'password' : 'text'" v-model.trim.lazy="userPasswd.confirm">
<span class="input-group-text input-group-addon lp-cursor-pointer" @click="showPasswd.confirm = !showPasswd.confirm"><i :class="['fa-regular',{'fa-eye-slash':!showPasswd.confirm},{'fa-eye':showPasswd.confirm}]"></i></span>
</div>
</div>
</div>
<div class="row mt-3">
<div class="col-lg-12 text-center">
<button type="button" class="btn border-3 btn-primary px-4" @click="updateUserPasswd(userPasswd)"><cn>保存</cn><en>Save</en></button>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-lg-12">
<div class="card">
<div class="card-header bg-transparent">
<div class="p-2 mb-0 d-flex align-items-end">
<cn>应用场景</cn>
<en>Application scenario</en>
</div>
</div>
<div class="card-body py-4">
<div class="row py-2">
<div class="col-lg-2 offset-lg-1 lp-align-center">
<label>
<cn>场景</cn>
<en>Scene</en>
</label>
</div>
<div class="col-lg-6">
<select class="form-select" v-if="Object.keys(videoBufferConf).length > 0" v-model="videoBufferConf.used">
<option v-for="item in handleSysScene" :value="item">{{item}}</option>
</select>
</div>
<div class="col-lg-2">
<button type="button" class="btn border-3 btn-primary change" @click="updateVideoBufferConf"><cn>切换</cn><en>Change</en></button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-6">
<div class="card">
<div class="card-header bg-transparent">
<div class="p-2 mb-0 d-flex align-items-end">
<cn>定时维护</cn>
<en>Auto reboot</en>
</div>
</div>
<div class="card-body py-4">
<div class="row">
<div class="col-lg-2 offset-lg-1 lp-align-center">
<label>
<cn>系统时间</cn>
<en>system time</en>
</label>
</div>
<div class="col-lg-6">
<input class="form-control" v-model.trim.lazy="sysTime">
</div>
<div class="col-lg-2">
<button type="button" class="btn border-3 btn-primary px-2" @click="syncTimeFromPc"><cn>本地同步</cn><en>Sync from PC</en></button>
</div>
</div>
<div class="row mt-3" v-if="Object.keys(ntpConf).length > 0">
<div class="col-lg-2 offset-lg-1 lp-align-center">
<label>
<cn>NTP同步</cn>
<en>NTP sync</en>
</label>
</div>
<div class="col-lg-3">
<input class="form-control" v-model.trim.lazy="ntpConf.server">
</div>
<div class="col-lg-3">
<div class="input-group">
<span class="input-group-text input-group-addon">
<cn>间隔</cn>
<en>inr</en>
</span>
<input class="form-control" v-model.trim.lazy="ntpConf.interval">
<span class="input-group-text input-group-addon">
<cn>分钟</cn>
<en>min</en>
</span>
</div>
</div>
<div class="col-lg-2">
<bs-switch v-model="ntpConf.enable"></bs-switch>
</div>
</div>
<div class="row mt-3">
<div class="col-lg-2 offset-lg-1 lp-align-center">
<label>
<cn>时区设置</cn>
<en>time zone</en>
</label>
</div>
<div class="col-lg-3">
<select class="form-select" v-model="timezoneConf.timeArea" @change="onTimeAreaChange">
<option value="Africa">Africa</option>
<option value="America">Americas</option>
<option value="Antarctica">Antarctica</option>
<option value="Asia">Asia</option>
<option value="Atlantic">Atlantic Ocean</option>
<option value="Australia">Australia</option>
<option value="Europe">Europe</option>
<option value="Indian">Indian Ocean</option>
<option value="Pacific">Pacific Ocean</option>
</select>
</div>
<div class="col-lg-3">
<select class="form-select" v-model="timezoneConf.timeCity">
<option v-for="item in timezoneCitys" :value="item.name">{{item.name}}</option>
</select>
</div>
</div>
<div class="row mt-3">
<div class="col-lg-2 offset-lg-1 lp-align-center">
<label>
<cn>维护时间</cn>
<en>reboot time</en>
</label>
</div>
<div class="col-lg-3">
<select class="form-select" v-model="cronDay">
<option cn="从不" en="never" value="x" v-language-option></option>
<option cn="每天" en="everyday" value="*" v-language-option></option>
<option cn="每周一" en="monday" value="1" v-language-option></option>
<option cn="每周二" en="tuesday" value="2" v-language-option></option>
<option cn="每周三" en="wednesday" value="3" v-language-option></option>
<option cn="每周四" en="thursday" value="4" v-language-option></option>
<option cn="每周五" en="friday" value="5" v-language-option></option>
<option cn="每周六" en="saturday" value="6" v-language-option></option>
<option cn="每周日" en="sunday" value="0" v-language-option></option>
</select>
</div>
<div class="col-lg-3">
<select class="form-select" v-model="cronTime">
<option value="0">0:00</option>
<option value="1">1:00</option>
<option value="2">2:00</option>
<option value="3">3:00</option>
<option value="4">4:00</option>
<option value="5">5:00</option>
<option value="6">6:00</option>
<option value="7">7:00</option>
<option value="8">8:00</option>
<option value="9">9:00</option>
<option value="10">10:00</option>
<option value="11">11:00</option>
<option value="12">12:00</option>
<option value="13">13:00</option>
<option value="14">14:00</option>
<option value="15">15:00</option>
<option value="16">16:00</option>
<option value="17">17:00</option>
<option value="18">18:00</option>
<option value="19">19:00</option>
<option value="20">20:00</option>
<option value="21">21:00</option>
<option value="22">22:00</option>
<option value="23">23:00</option>
</select>
</div>
</div>
<div class="row mt-4">
<div class="col-lg-12 text-center">
<button type="button" class="btn border-3 btn-primary px-4 me-4" @click="saveSysConf"><cn>保存</cn><en>Save</en></button>
<button type="button" class="btn border-3 btn-primary px-4 me-4" @click="rebootConfirm('是否立即重启系统')"><cn>立即重启</cn><en>Reboot</en></button>
<button type="button" class="btn border-3 btn-primary px-4" @click="resetConfirm"><cn>恢复出厂设置</cn><en>Reset default</en></button>
</div>
</div>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="card">
<div class="card-header bg-transparent">
<div class="p-2 mb-0 d-flex align-items-end">
<cn>配置文件</cn>
<en>Config file</en>
<small>
<cn>按需导出配置文件</cn>
<en>Export configuration files on demand</en>
</small>
</div>
</div>
<div class="card-body py-4">
<div class="row row-cols-4 row-cols-lg-4 px-5">
<div class="text-center">
<cn>编解码配置</cn>
<en>Default Config</en>
</div>
<div class="text-center">
<cn>布局配置</cn>
<en>Layout Config</en>
</div>
<div class="text-center">
<cn>推流配置</cn>
<en>Push Config</en>
</div>
<div class="text-center">
<cn>密码配置</cn>
<en>Password Config</en>
</div>
</div>
<hr>
<div>
<div class="row row-cols-4 row-cols-lg-4 px-5">
<div class="lp-align-center">
<bs-switch v-model="exportConfigs.config"></bs-switch>
</div>
<div class="lp-align-center">
<bs-switch v-model="exportConfigs.defLays"></bs-switch>
</div>
<div class="lp-align-center">
<bs-switch v-model="exportConfigs.push"></bs-switch>
</div>
<div class="lp-align-center">
<bs-switch v-model="exportConfigs.passwd"></bs-switch>
</div>
</div>
</div>
<div class="row row-cols-4 row-cols-lg-4 mt-4 px-5">
<div class="text-center">
<cn>录制配置</cn>
<en>Record Config</en>
</div>
<div class="text-center">
<cn>端口配置</cn>
<en>Port Config</en>
</div>
<div class="text-center">
<cn>维护配置</cn>
<en>NTP Config</en>
</div>
<div class="text-center">
<cn>场景配置</cn>
<en>Scene Config</en>
</div>
</div>
<hr>
<div>
<div class="row row-cols-4 row-cols-lg-4 px-5">
<div class="lp-align-center">
<bs-switch v-model="exportConfigs.record"></bs-switch>
</div>
<div class="lp-align-center">
<bs-switch v-model="exportConfigs.port"></bs-switch>
</div>
<div class="lp-align-center">
<bs-switch v-model="exportConfigs.cron"></bs-switch>
</div>
<div class="lp-align-center">
<bs-switch v-model="exportConfigs.videoBuffer"></bs-switch>
</div>
</div>
</div>
<div class="row mt-4">
<div class="col-lg-12 text-center mt-3">
<button type="button" class="btn border-3 btn-primary px-4 me-3" @click="exportConf"><cn>导出</cn><en>Export</en></button>
<button type="button" class="btn border-3 btn-primary px-4 " @click="importConf"><cn>导入</cn><en>Import</en></button>
<input type="file" accept=".zip" style="display:none;" ref="importHandle" />
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row" v-if="Object.keys(hardwareConf).length > 0 && hardwareConf.function.portCtrl">
<div class="col-lg-12">
<div class="card">
<div class="card-header bg-transparent">
<div class="p-2 mb-0 d-flex align-items-end">
<cn>端口配置</cn>
<en>Port config</en>
</div>
</div>
<div class="card-body" v-if="Object.keys(portConf).length > 0">
<div class="row mb-2">
<div class="col-10">
<div class="row">
<div class="col-3"></div>
<div class="col text-center">HTTP</div>
<div class="col text-center">RTSP</div>
<div class="col text-center">RTMP</div>
<div class="col text-center">HTTPTS</div>
<div class="col text-center">Telnet</div>
<div class="col text-center">SSH</div>
</div>
</div>
</div>
<hr>
<div class="row mt-2">
<div class="col-10">
<div class="row">
<div class="col-3 d-flex align-items-center justify-content-end">
<cn>固定端口</cn>
<en>Static port</en>
</div>
<div class="col">
<input type="text" class="form-control text-center" v-model.trim.lazy="portConf.http[0]" readonly disabled>
</div>
<div class="col">
<input type="text" class="form-control text-center" v-model.trim.lazy="portConf.rtsp[0]" readonly disabled>
</div>
<div class="col">
<input type="text" class="form-control text-center" v-model.trim.lazy="portConf.rtmp[0]" readonly disabled>
</div>
<div class="col">
<input type="text" class="form-control text-center" v-model.trim.lazy="portConf.httpts[0]" readonly disabled>
</div>
<div class="col">
<input type="text" class="form-control text-center" v-model.trim.lazy="portConf.telnet[0]" readonly disabled>
</div>
<div class="col">
<input type="text" class="form-control text-center" v-model.trim.lazy="portConf.ssh[0]" readonly disabled>
</div>
</div>
<div class="row mt-3">
<div class="col-3 d-flex align-items-center justify-content-end">
<cn>备用端口</cn>
<en>Reserve port</en>
</div>
<div class="col">
<input type="text" class="form-control text-center" v-model.trim.lazy="portConf.http[1]">
</div>
<div class="col">
<input type="text" class="form-control text-center" v-model.trim.lazy="portConf.rtsp[1]">
</div>
<div class="col">
<input type="text" class="form-control text-center" v-model.trim.lazy="portConf.rtmp[1]">
</div>
<div class="col">
<input type="text" class="form-control text-center" v-model.trim.lazy="portConf.httpts[1]">
</div>
<div class="col">
<input type="text" class="form-control text-center" v-model.trim.lazy="portConf.telnet[1]">
</div>
<div class="col">
<input type="text" class="form-control text-center" v-model.trim.lazy="portConf.ssh[1]">
</div>
</div>
<div class="row mt-3">
<div class="col-3 d-flex align-items-center justify-content-end">
<cn>映射(外网)端口</cn>
<en>NAT port</en>
</div>
<div class="col">
<input type="text" class="form-control text-center" v-model.trim.lazy="portConf.http[2]">
</div>
<div class="col">
<input type="text" class="form-control text-center" v-model.trim.lazy="portConf.rtsp[2]">
</div>
<div class="col">
<input type="text" class="form-control text-center" v-model.trim.lazy="portConf.rtmp[2]">
</div>
<div class="col">
<input type="text" class="form-control text-center" v-model.trim.lazy="portConf.httpts[2]">
</div>
<div class="col">
<input type="text" class="form-control text-center" v-model.trim.lazy="portConf.telnet[2]">
</div>
<div class="col">
<input type="text" class="form-control text-center" v-model.trim.lazy="portConf.ssh[2]">
</div>
</div>
</div>
</div>
<div class="row my-2">
<div class="col-lg-12 text-center mt-3">
<button type="button" class="btn border-3 btn-primary px-5" @click="updatePortConf"><cn>保存</cn><en>Save</en></button>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-6">
<div class="row">
<div class="col-lg-12">
<div class="card">
<div class="card-header bg-transparent">
<div class="p-2 mb-0 d-flex align-items-end">
<cn>远程协助</cn>
<en>Remote Assistance</en>
</div>
</div>
<div class="card-body">
<div class="row my-3">
<div class="col-lg-3 d-flex align-items-center justify-content-end">
<cn>授权码</cn>
<en>Auth code</en>
</div>
<div class="col-lg-3 lp-align-center">
<input type="text" class="form-control" v-model.trim.lazy="helpCode" readonly disabled>
</div>
<div class="col-lg-6">
<button type="button" class="btn border-3 btn-primary px-4 me-2" @click="startHelp"><cn>开始协助</cn><en>Start</en></button>
<button type="button" class="btn border-3 btn-primary px-4" @click="stopHelp"><cn>停止协助</cn><en>Stop</en></button>
</div>
</div>
</div>
</div>
</div>
<div class="col-lg-12">
<div class="card">
<div class="card-header bg-transparent">
<div class="p-2 mb-0 d-flex align-items-end">
<cn>网络测试</cn>
<en>Network Test</en>
</div>
</div>
<div class="card-body">
<div class="row my-3">
<div class="col-lg-3 d-flex align-items-center justify-content-end">
<cn>服务地址</cn>
<en>Server</en>
</div>
<div class="col-lg-5 lp-align-center" v-if="Object.keys(netManagerConf).length > 0">
<input type="text" class="form-control" v-model.trim.lazy="netManagerConf.onlineServer">
</div>
<div class="col-lg-4">
<button type="button" class="btn border-3 btn-primary px-3 me-2" @click="updateNetManagerConf"><cn>保存</cn><en>Save</en></button>
<button type="button" class="btn border-3 btn-primary px-4 net-test" @click="systemNetTest"><cn>网络测试</cn><en>Start Test</en></button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="card">
<div class="card-header bg-transparent">
<div class="p-2 mb-0 d-flex align-items-end">
<cn>系统升级</cn><en>Upgrade</en>
</div>
</div>
<div class="card-body" v-if="Object.keys(versionConf).length > 0">
<div class="row mt-1">
<div class="col-lg-2 offset-lg-1 lp-align-center">
<label>
<cn>应用版本</cn>
<en>App version</en>
</label>
</div>
<div class="col-lg-6">
<label>{{versionConf.app}}</label>
</div>
</div>
<div class="row mt-3">
<div class="col-lg-2 offset-lg-1 lp-align-center">
<label>
<cn>SDK版本</cn>
<en>SDK version</en>
</label>
</div>
<div class="col-lg-6">
<label>{{versionConf.sdk}}</label>
</div>
</div>
<div class="row mt-3">
<div class="col-lg-2 offset-lg-1 lp-align-center">
<label>
<cn>系统版本</cn>
<en>Sys version</en>
</label>
</div>
<div class="col-lg-6">
<label>{{versionConf.sys}}</label>
</div>
</div>
<hr class="my-4">
<div class="row mt-3">
<div class="col-lg-2 offset-lg-1 lp-align-center">
<label>
<cn>上传升级</cn>
<en>upload packet</en>
</label>
</div>
<div class="col-lg-6">
<button type="button" class="btn border-3 btn-primary px-3 me-2" @click="showBootstrapModal('upload')"><cn>选择文件</cn><en>File</en></button>
<button type="button" class="btn border-3 btn-primary px-3" @click="showBootstrapModal('log')"><cn>版本日志</cn><en>Logs</en></button>
</div>
</div>
<div class="row mt-1 mb-4">
<div class="col-lg-2 offset-lg-1 lp-align-center">
<label>
<cn>在线升级</cn>
<en>online update</en>
</label>
</div>
<div class="col-lg-6">
<loading-button custom-class="btn border-3 btn-primary px-3 me-2 checkUpdate" @button-click="checkUpdatePatch" :had-loading="checkLoading">
<cn>检测更新</cn>
<en>Search</en>
</loading-button>
<button type="button" class="btn border-3 btn-primary px-3 search-packet checkUpdate" @click="searchUpdatePatch"><i class="fa fa-search"></i></button>
</div>
</div>
</div>
</div>
</div>
</div>
<upload-modal modal-title="上传升级包&Upload" :modal-show="showUploadModal" modal-fade="true"
upload-allow="['bin']" upload-action="/link/upd/uploadPatch.php" upload-count="1"
upload-tip="请把升级包拖动到此处...&Please drag the upgrade package here..."
@upload-success="uploadSuccess" @upload-error="uploadError">
</upload-modal>
<logger-modal :modal-size="'modal-lg'" :had-header="false" :had-footer="false" :modal-show="showVerLogModal" :modal-fade="true" :content-class="'logCtx'" :body-class="'logBody'" >
<div data-simplebar class="mt-2">
<div v-for="(item,index) in verLogsConf" :key="index" class="mt-3">
<button v-if="index===0" type="button" class="btn-clear close" @click="showBootstrapModal('log')"><i class="fa-solid fa-x"></i></button>
<div class="row">
<div class="col-lg-10 offset-lg-1">
<h3>{{item.version}}</h3>
</div>
</div>
<div class="row mt-2">
<div class="col-lg-10 offset-lg-1">
<ul>
<li v-for="(it,idx) in item.logs" :key="idx" style="font-size: 14px;white-space:pre-wrap;">{{it}}</li>
</ul>
</div>
</div>
<hr>
</div>
</div>
</logger-modal>
<search-modal :modal-title="'固件搜索&Search'" :modal-show="showSearchModal"
:confirm-btn-name="'搜索&Search'" :cancel-btn-name="'取消&Cancel'"
@confirm-btn-click="searchPatchBySn">
<div class="row my-3">
<div class="col-lg-3 offset-lg-1">
<div class="snTitle">
<cn>固件编号:</cn>
<en>Patch Serial:</en>
</div>
</div>
<div class="col-lg-6">
<input class="snInput" v-model.trim.lazy="patchSN" autocomplete="off"/>
</div>
</div>
</search-modal>
<upgrade-modal v-model:check-upgrade="checkUpgrade" :patch-sn="patchSN" modal-fade="true"></upgrade-modal>
</main>
</div>
<?php include ("./public/foot.inc") ?>
<script src="assets/plugins/fileinput/js/fileinput.min.js" type="module"></script>
<script src="assets/plugins/fileinput/js/locales/zh.js" type="module"></script>
<script src="assets/plugins/fileinput/themes/fa6/theme.min.js" type="module"></script>
<script type="module">
import vue from "./assets/js/vue.build.js";
import JsZip from "./assets/plugins/jszip/jszip.esm.js"
import * as fileSave from "./assets/plugins/jszip/filesaver.esm.js";
import { rpc2,alertMsg,func,queryData,popover,formatDate,rebootConfirm,resetConfirm,clearReactiveObject } from "./assets/js/lp.utils.js";
import { useHardwareConf,useNetManagerConf,usePasswordConf,useVideoBufferConf,useNtpConf,useTimezoneConf,usePortConf,useVersionConf,useVerLogsConf,useWpaConf } from "./assets/js/vue.hooks.js";
import { ignoreCustomElementPlugin,bootstrapSwitchComponent,languageOptionDirective,uploadModalComponent,upgradeModalComponent,customModalComponent,loadingButtonComponent } from "./assets/js/vue.helper.js"
import { wifiFlagComponent,antenanFlagComponent } from "./assets/js/vue.flags.js";
const { createApp,ref,reactive,watch,watchEffect,computed,onMounted } = vue;
const app = createApp({
directives:{
"language-option": languageOptionDirective
},
components:{
"bs-switch" : bootstrapSwitchComponent,
"wifi-flag": wifiFlagComponent,
"antenan-flag": antenanFlagComponent,
"upload-modal": uploadModalComponent,
"logger-modal": customModalComponent,
"upgrade-modal": upgradeModalComponent,
"search-modal": customModalComponent,
"loading-button": loadingButtonComponent
},
setup(props,context) {
const { hardwareConf } = useHardwareConf();
const { netManagerConf,updateNetManagerConf } = useNetManagerConf();
const { wpaConf } = useWpaConf();
const { updateUserPasswd } = usePasswordConf();
const { videoBufferConf,updateVideoBufferConf } = useVideoBufferConf();
const { ntpConf } = useNtpConf();
const { timezoneConf } = useTimezoneConf();
const { portConf,updatePortConf } = usePortConf();
const { versionConf } = useVersionConf();
const { verLogsConf } = useVerLogsConf();
const { saveAs } = fileSave;
const state = {
netAdapter: reactive({}),
wifiHandler: ref(null),
wifiPopover: {},
antenanHandler:ref(null),
antenanPopover: {},
wifiRefresh: ref(false),
wifiList:reactive([]),
wifiPassword:ref(""),
wifiConnectId:ref(""),
sysTime: ref("1970-01-01 08:00:00"),
timezoneCitys: reactive([]),
cronDay: ref("never"),
cronTime: ref("0"),
importHandle:ref(null),
helpCode:ref(""),
userPasswd:reactive({}),
showPasswd:reactive({}),
exportConfigs:reactive({}),
showUploadModal:ref(false),
showVerLogModal:ref(false),
showSearchModal:ref(false),
checkLoading:ref(false),
checkUpgrade:ref(false),
patchSN:ref(""),
macLock:ref(true)
}
watch(state.wifiHandler,() => {
if(state.wifiHandler.value) {
if(Object.keys(state.wifiPopover).length > 0) {
state.wifiPopover.dispose();
state.wifiPopover = {};
}
state.wifiPopover = popover(state.wifiHandler.value, {
placement:"bottom",
trigger:"hover",
content:`<cn>请先插入USB WIFI网卡</cn><en>Please insert the USB WIFI network card first</en>`,
});
if(document.querySelector('a[href="#tab1"]'))
document.querySelector('a[href="#tab1"]').click();
} else {
if(!state.wifiHandler.value && Object.keys(state.wifiPopover).length !== 0) {
state.wifiPopover.dispose();
state.wifiPopover = {};
}
}
})
watch(state.antenanHandler, () => {
if(state.antenanHandler.value) {
if(Object.keys(state.antenanPopover).length > 0) {
state.antenanPopover.dispose();
state.antenanPopover = {};
}
state.antenanPopover = popover(state.antenanHandler.value, {
placement:"bottom",
trigger:"hover",
content:`<cn>请先插入移动网卡</cn><en>Please insert the Cellular network card first</en>`,
})
if(document.querySelector('a[href="#tab1"]'))
document.querySelector('a[href="#tab1"]').click();
} else {
if(!state.antenanHandler.value && Object.keys(state.antenanPopover).length !== 0) {
state.antenanPopover.dispose();
state.antenanPopover = {};
}
}
})
watchEffect(()=>{
if(!state.checkUpgrade.value)
state.checkLoading.value = false;
if(wpaConf.length > 0 && state.wifiConnectId.value === "") {
state.wifiConnectId.value = wpaConf[0].ssid
state.wifiPassword.value = wpaConf[0].psk;
}
if(Object.keys(timezoneConf).length > 0 && state.timezoneCitys.length === 0)
onTimeAreaChange();
})
const getAdapterNetState = () => {
rpc2("net.getState").then(data => {
clearReactiveObject(state.netAdapter);
const ignoreAry = [];
if(hardwareConf.fac === "ENC5V2") {
Object.values(data.interface).forEach(item => {
if(item.type === "lan" && item.dev.includes("eth"))
ignoreAry.push(item.dev);
})
}
ignoreAry.forEach(item => {
if(item !== "eth0")
delete data.interface[item];
})
Object.assign(state.netAdapter,data.interface);
});
setTimeout(getAdapterNetState,2000);
}
const handleSysScene = computed(()=>{
return Object.keys(videoBufferConf).filter((item,index)=>{
return item !== "used";
})
});
const updateDefNetwork = (dev) => {
updateNetManagerConf().then(()=>{
if(dev === "eth0")
setTimeout(() => window.location.href="http://"+netManagerConf["interface"]["eth0"]["ip"]+"/sys.php",1000)
})
}
const enableWifi = state => {
updateNetManagerConf("noTip").then(()=>{
if(state)
alertMsg('<cn>WIFI已启用</cn><en>WIFI enable successfully!</en>', 'success');
else
alertMsg('<cn>WIFI已关闭</cn><en>WIFI disable successfully!</en>', 'success');
})
}
const refreshWifi = (tip = 'loading',refresh = true) => {
const scanwifi = type => {
rpc2("net.scanWifi",[refresh]).then(data => {
if(data.length === 0)
data.push({ssid:state.wifiConnectId});
state.wifiList.splice(0, state.wifiList.length, ...data);
state.wifiRefresh.value = false;
})
}
if(tip === "noLoading") {
scanwifi(refresh);
return;
}
state.wifiRefresh.value = true;
setTimeout(scanwifi,2000);
}
const connectWifi = () => {
state.wifiPassword.value = state.wifiPassword.value.replace(/\s/g, '');
if(state.wifiPassword.value.length < 8) {
alertMsg('<cn>WIFI密码格式错误密码长度不能少于8位</cn><en>The wifi password format is wrong. The password length cannot be less than 8 characters!</en>', 'error');
return;
}
rpc2("net.setSimpleWifi", [state.wifiConnectId.value,state.wifiPassword.value]).then(data => {
if(data)
alertMsg('<cn>设置成功如果长时间处于连接状态请检查wifi密码是否正确</cn><en>The setting is successful. If you are connected for a long time, please check whether the wifi password is correct</en>', 'success');
else
alertMsg('<cn>设置失败</cn><en>Save failed!</en>', 'error');
});
}
const formatNetSpeed = speed => {
if (speed < 1024) {
return speed + " kb/s";
} else {
const formattedSpeed = parseFloat(speed / 1024).toFixed(1);
return formattedSpeed.replace(/\.0$/, '') + " mb/s";
}
}
const syncTimeFromPc = () => {
let time1 = formatDate("yyyy/MM/dd/hh/mm/ss");
let time2 = formatDate("yyyy-MM-dd hh:mm:ss");
func("/system/setSystemTime",{time1:time1,time2:time2}).then((data)=>{
if(data.status === "success") {
alertMsg('<cn>时间同步成功</cn><en>system time synchronization successful!</en>', 'success');
state.sysTime.value = time2;
}
})
}
const onTimeAreaChange = (evt) => {
queryData("/timezone/zoneinfo/"+timezoneConf.timeArea+"/").then(data=>{
state.timezoneCitys.splice(0, state.timezoneCitys.length, ...data);
if(evt !== undefined)
timezoneConf.timeCity = state.timezoneCitys[0].name;
})
}
const saveSysConf = () => {
Promise.all([
func("/conf/updateNtpConf", ntpConf),
func("/conf/updateTimezoneConf", timezoneConf),
func("/system/setSystemCrontab", { day: state.cronDay.value, time: state.cronTime.value }),
]).then((results) => {
if(results.every(ret => typeof ret === "boolean" ? ret : (ret?.status === "success")))
alertMsg('<cn>保存设置成功</cn><en>Save config successfully!</en>', 'success');
else
alertMsg('<cn>保存设置失败</cn><en>Save config failed!</en>', 'error');
})
}
const exportConf = () => {
let confs = ["lang.json"];
for(let i=0;i<Object.keys(state.exportConfigs).length;i++) {
let path = Object.keys(state.exportConfigs)[i];
if(state.exportConfigs[path]) {
if(path === "cron") {
confs.push("ntp.json");
confs.push("auto/root.cron");
confs.push("misc/timezone/tzselect.json");
} else if(path === "videoBuffer") {
confs.push("board.json");
confs.push("videoBuffer.json");
} else {
confs.push(path+".json");
}
}
}
const promiseArray = confs.map((conf) => {
return queryData("config/" + conf, { responseType: 'blob' }).then(data => ({ name: conf, data }));
});
Promise.all(promiseArray)
.then((results) => {
const zip = new JsZip();
results.forEach(({ name, data }) => {
zip.file(name, data, { binary: true });
});
if (Object.keys(zip.files).length > 0) {
return zip.generateAsync({ type: 'blob' });
} else {
throw new Error('下载全部失败');
}
})
.then(blob => {
saveAs(blob, 'configs.zip');
})
.catch(error => {
console.error(error);
});
}
const importConf = () =>{
state.importHandle.value.click();
state.importHandle.value.addEventListener('change', event => {
let data = new FormData();
let file=event.target.files[0];
let name=file.name;
data.append("file",file);
data.append("name",name);
axios({
url: '/link/upd/uploadConf.php',
method: 'post',
data: data,
responseType: 'json',
headers: {'Content-Type': 'multipart/form-data'}
}).then(response => {
if (response.data.isSuccess) {
alertMsg('<cn>导入成功</cn><en>Import success</en>','success');
} else {
alertMsg('<cn>导入失败</cn><en>Import faild</en>','error');
}
})
.catch(error => {
console.error('请求发生错误:', error);
});
});
}
const startHelp = () => {
state.helpCode.value = Math.floor(Math.random()*1000);
func("/system/startHelp",{helpCode: state.helpCode.value}).then((data)=>{
if(data.status === "success")
alertMsg('<cn>连接成功,请向客服提供授权码以便控制您的编码器。</cn><en>Connect success, please provide auth code to customer service to control your encoder!</en>', 'success');
})
}
const stopHelp = () => {
func("/system/stopHelp").then((data)=>{
if(data.status === "success") {
state.helpCode.value = "";
alertMsg('<cn>已断开连接</cn><en>Disconnect success</en>', 'success');
}
})
}
const systemNetTest = () => {
func("/system/systemNetTest",netManagerConf.onlineServer).then(data => {
const str = data.data.join();
if(str === "")
alertMsg('<cn>域名解析超时</cn><en>DNS timeout</en>!', 'error');
else if(str.indexOf(" 0%")>0)
alertMsg('<cn>网络可用</cn><en>Network available</en>', 'success');
else
alertMsg('<cn>网络不可用</cn><en>Network Unavailable</en>', 'error');
})
}
const showBootstrapModal = type => {
if(type === "upload")
state.showUploadModal.value = !state.showUploadModal.value;
if(type === "log")
state.showVerLogModal.value = !state.showVerLogModal.value;
}
const uploadSuccess = data => {
const response = data.response;
if(response.upload === "0")
rebootConfirm("上传成功,是否立即重启系统完成更新?");
if(response.upload === "-1")
alertMsg("<cn>上传失败,升级包机型不匹配!</cn><en>Upload failed, upgrade package model does not match!</en>","error");
if(response.upload === "-2")
alertMsg("<cn>上传失败,升级包与系统版本不匹配!</cn><en>Upload failed, the upgrade package does not match the system versio!</en>","error");
}
const uploadError = errMsg => {
alertMsg(errMsg, 'error');
}
const checkUpdatePatch = () => {
state.patchSN.value = "";
state.checkLoading.value = true;
setTimeout(()=>{
state.checkUpgrade.value = true;
},1000)
}
const searchUpdatePatch = () => {
state.showSearchModal.value = !state.showSearchModal.value;
}
const searchPatchBySn = () => {
if(state.patchSN.value === "") {
alertMsg("<cn>请输入固件编号</cn><en>Please enter the patch sn.</en>", "error");
return;
}
state.checkUpgrade.value = true;
state.showSearchModal.value = false;
}
const updateRenderData = () => {
Object.assign(state.userPasswd,{oldpwd: '',newpwd: '',confirm: ''});
Object.assign(state.showPasswd,{wifipwd: false,oldpwd: false,newpwd: false,confirm: false});
Object.assign(state.exportConfigs,{config: true,defLays: false,push: false,
record: false,port: false,passwd: false,videoBuffer: true,cron: true
});
}
const getSysAbortTime = () => {
func("/system/getSystemTime").then(result => {
state.sysTime.value = result.data;
setInterval(()=>{
let currentTime = new Date(state.sysTime.value);
currentTime.setSeconds(currentTime.getSeconds() + 1);
const year = currentTime.getFullYear();
const month = String(currentTime.getMonth() + 1).padStart(2, '0');
const day = String(currentTime.getDate()).padStart(2, '0');
const hours = String(currentTime.getHours()).padStart(2, '0');
const minutes = String(currentTime.getMinutes()).padStart(2, '0');
const seconds = String(currentTime.getSeconds()).padStart(2, '0');
state.sysTime.value = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
},1000);
});
func("/system/getSystemCrontab").then(result => {
if ( result.data === null || result.data.split( " " ).length !== 6 ) {
state.cronDay.value = "x";
state.cronTime.value = "0";
} else {
state.cronTime.value = result.data.split( " " )[ 1 ];
state.cronDay.value = result.data.split( " " )[ 4 ];
}
});
}
onMounted(()=>{
updateRenderData();
getSysAbortTime();
getAdapterNetState();
refreshWifi("noLoading",false);
})
return {...state,hardwareConf,netManagerConf,videoBufferConf,ntpConf,timezoneConf,portConf,versionConf,verLogsConf,
enableWifi,refreshWifi,connectWifi,updateNetManagerConf,handleSysScene, updateUserPasswd,updateVideoBufferConf,updateDefNetwork,
updatePortConf,showBootstrapModal,formatNetSpeed, uploadSuccess,uploadError,rebootConfirm,resetConfirm, onTimeAreaChange,
syncTimeFromPc,saveSysConf,exportConf,importConf,startHelp,stopHelp, systemNetTest,checkUpdatePatch,searchUpdatePatch,searchPatchBySn}
}
});
app.use(ignoreCustomElementPlugin);
app.mount('#app');
</script>
</body>
</html>