<template>
|
<div id="configMain">
|
<div class="config-container">
|
<div class="config-tabs">
|
<el-tabs v-model="activeTab" type="card" @tab-click="handleClick">
|
<!-- 基础配置 -->
|
<el-tab-pane :label="$t('config.basicConfiguration')" name="base">
|
<div class="config-grid">
|
<!-- 显示设置卡片 -->
|
<el-card class="config-card card-theme-display">
|
<div class="config-card__header">
|
<i class="el-icon-monitor config-card__header-icon"></i>
|
{{ $t('config.displaySettings') }}
|
</div>
|
<div class="config-card__body">
|
<el-form ref="displayForm" :model="base" :rules="baseRules" label-width="140px">
|
<!-- 自动调节亮度 -->
|
<!-- <div class="config-form-item">
|
<el-form-item :label="$t('config.autoAdjustScreenBrightness')">
|
<el-switch v-model="base.brightnessAuto" :active-value="1" :inactive-value="0"
|
active-color="#1890ff" inactive-color="#ff4949">
|
</el-switch>
|
</el-form-item>
|
</div> -->
|
<!-- 屏幕亮度 -->
|
<div class="config-form-item">
|
<el-form-item :label="$t('config.screenBrightness')">
|
<div class="compact-controls">
|
<el-slider v-model="base.backlight" :max="100" :min="1" style="width:60%;"></el-slider>
|
<span class="value-display">{{ base.backlight }}%</span>
|
</div>
|
</el-form-item>
|
</div>
|
<!-- 自动息屏 -->
|
<div class="config-form-item">
|
<el-form-item :label="$t('config.autoTurnOffScreenTime')" prop="screenOff">
|
<el-select v-model="base.screenOff" placeholder="请选择">
|
<!-- <el-option v-for="(item, index) in idleData" :key="index" :label="item" :value="index">
|
</el-option> -->
|
<el-option :label="$t('config.never')" :value="0"></el-option>
|
<el-option :label="$t('config.min1')" :value="1"></el-option>
|
<el-option :label="$t('config.min2')" :value="2"></el-option>
|
<el-option :label="$t('config.min3')" :value="3"></el-option>
|
<el-option :label="$t('config.min4')" :value="4"></el-option>
|
<el-option :label="$t('config.min5')" :value="5"></el-option>
|
</el-select>
|
<!-- <el-input v-model="base.screenOff" type="number" :min="0"
|
:placeholder="$t('config.msg_please_enter')"></el-input><span>
|
{{ $t('config.min') }}</span> -->
|
</el-form-item>
|
</div>
|
<!-- 自动屏保 -->
|
<div class="config-form-item">
|
<el-form-item :label="$t('config.autoScreenSaverTime')" prop="screensaver">
|
<el-select v-model="base.screensaver" placeholder="请选择">
|
<!-- <el-option v-for="(item, index) in idleData" :key="index" :label="item" :value="index">
|
</el-option> -->
|
<el-option :label="$t('config.never')" :value="0"></el-option>
|
<el-option :label="$t('config.min1')" :value="1"></el-option>
|
<el-option :label="$t('config.min2')" :value="2"></el-option>
|
<el-option :label="$t('config.min3')" :value="3"></el-option>
|
<el-option :label="$t('config.min4')" :value="4"></el-option>
|
<el-option :label="$t('config.min5')" :value="5"></el-option>
|
</el-select>
|
</el-form-item>
|
</div>
|
<!-- 白色补光灯 -->
|
<div class="config-form-item"
|
v-if="sys.model == 'vf105' || sys.model == 'vf107' || sys.model == 'vf114' || sys.model == 'vf124' || sys.model == 'vf202'">
|
<el-form-item :label="$t('config.brightness')">
|
<div class="compact-controls">
|
<el-slider v-model="base.brightness" :max="100" :min="0" style="width:60%;"></el-slider>
|
<span class="value-display">{{ base.brightness }}%</span>
|
</div>
|
</el-form-item>
|
</div>
|
<!-- 红外补光灯 -->
|
<!-- <div class="config-form-item">
|
<el-form-item :label="$t('config.nirBrightness')">
|
<div class="compact-controls">
|
<el-slider v-model="base.nirBrightness" :max="100" :min="0" style="width:60%;"></el-slider>
|
<span class="value-display">{{ base.nirBrightness }}%</span>
|
</div>
|
</el-form-item>
|
</div> -->
|
</el-form>
|
</div>
|
</el-card>
|
<!-- 信息显示卡片 -->
|
<el-card class="config-card card-theme-display">
|
<div class="config-card__header">
|
<i class="el-icon-info config-card__header-icon"></i>
|
{{ $t('config.informationDisplay') }}
|
</div>
|
<div class="config-card__body">
|
<el-form ref="infoForm" :model="base" label-width="140px">
|
<!-- 显示SN -->
|
<div class="config-form-item">
|
<el-form-item :label="$t('config.displayDeviceSn')">
|
<el-switch v-model="base.showSn" :active-value="1" :inactive-value="0" active-color="#1890ff"
|
inactive-color="#ff4949">
|
</el-switch>
|
</el-form-item>
|
</div>
|
<!-- 显示IP -->
|
<div class="config-form-item">
|
<el-form-item :label="$t('config.displayIp')">
|
<el-switch v-model="base.showIp" :active-value="1" :inactive-value="0" active-color="#1890ff"
|
inactive-color="#ff4949">
|
</el-switch>
|
</el-form-item>
|
</div>
|
<!-- 显示小程序码 -->
|
<!-- <div class="config-form-item">
|
<el-form-item :label="$t('config.displayCode')">
|
<el-switch v-model="base.showProgramCode" :active-value="1" :inactive-value="0"
|
active-color="#1890ff" inactive-color="#ff4949">
|
</el-switch>
|
</el-form-item>
|
</div> -->
|
<!-- 云证功能菜单 -->
|
<!-- <div class="config-form-item">
|
<el-form-item :label="$t('config.displayIdentityCard')">
|
<el-switch v-model="base.showIdentityCard" :active-value="1" :inactive-value="0"
|
active-color="#1890ff" inactive-color="#ff4949">
|
</el-switch>
|
</el-form-item>
|
</div> -->
|
</el-form>
|
</div>
|
</el-card>
|
<!-- 音频设置卡片 -->
|
<el-card class="config-card card-theme-display">
|
<div class="config-card__header">
|
<i class="el-icon-mic config-card__header-icon"></i>
|
{{ $t('config.audioSettings') }}
|
</div>
|
<div class="config-card__body">
|
<el-form ref="audioForm" :model="base" label-width="140px">
|
<!-- 音量 -->
|
<div class="config-form-item">
|
<el-form-item :label="$t('config.volume')" prop="volume">
|
<div class="compact-controls">
|
<el-slider v-model="base.volume" :max="10" :min="0" style="flex: 1"></el-slider>
|
<span class="value-display">{{ base.volume }}</span>
|
</div>
|
</el-form-item>
|
</div>
|
</el-form>
|
</div>
|
</el-card>
|
<!-- 语言与主题卡片 -->
|
<el-card class="config-card card-theme-display">
|
<div class="config-card__header">
|
<i class="el-icon-s-operation config-card__header-icon"></i>
|
{{ $t('config.languageAndThemes') }}
|
</div>
|
<div class="config-card__body">
|
<el-form ref="systemForm" :model="base" label-width="140px">
|
<div class="config-form-item">
|
<el-form-item :label="$t('config.language')" prop="language">
|
<el-radio-group v-model="base.language" class="language-radio-group"
|
:style="{ 'margin-bottom': version == 0 ? '0px' : '12px' }">
|
<el-radio v-if="version == 0" label="CN">{{ $t('config.cn') }}</el-radio>
|
<div v-else>
|
<el-radio label="EN">{{ $t('config.en') }}</el-radio>
|
<el-radio label="ES">{{ $t('config.es') }}</el-radio>
|
<el-radio label="FR">{{ $t('config.fr') }}</el-radio>
|
<el-radio label="DE">{{ $t('config.de') }}</el-radio>
|
<el-radio label="RU">{{ $t('config.ru') }}</el-radio>
|
<el-radio label="AR">{{ $t('config.ar') }}</el-radio>
|
<el-radio label="PT">{{ $t('config.pt') }}</el-radio>
|
<el-radio label="KO">{{ $t('config.ko') }}</el-radio>
|
</div>
|
</el-radio-group>
|
</el-form-item>
|
</div>
|
<!-- 工作主题 -->
|
<div class="config-form-item">
|
<el-form-item :label="$t('config.themeMode')">
|
<el-radio-group v-model="base.appMode">
|
<el-radio :label="0">{{ $t('config.standardMode') }}</el-radio>
|
<el-radio :label="1">{{ $t('config.simpleMode') }}</el-radio>
|
</el-radio-group>
|
</el-form-item>
|
</div>
|
<!-- 是否第一次登录后台 -->
|
<!-- <div class="config-form-item">
|
<el-form-item :label="$t('config.firstLogin')">
|
<el-radio-group v-model="base.firstLogin">
|
<el-radio :label="0">{{ $t('config.no') }}</el-radio>
|
<el-radio :label="1">{{ $t('config.yes') }}</el-radio>
|
</el-radio-group>
|
</el-form-item>
|
</div> -->
|
</el-form>
|
</div>
|
</el-card>
|
</div>
|
</el-tab-pane>
|
<!-- 网络配置 -->
|
<el-tab-pane :label="$t('config.networkConfiguration')" name="net">
|
<div class="config-grid">
|
<!-- 网络类型卡片 -->
|
<el-card class="config-card card-theme-display">
|
<div class="config-card__header">
|
<i class="el-icon-connection config-card__header-icon"></i>
|
{{ $t('config.networkType') }}
|
</div>
|
<div class="config-card__body">
|
<el-form ref="networkForm" :model="net" :rules="netRules" label-width="140px">
|
<div class="config-form-item">
|
<el-form-item :label="$t('config.networkType')">
|
<el-radio-group v-model="net.type">
|
<el-radio :label="1">{{ $t('config.ethernet') }}</el-radio>
|
<el-radio :label="2"
|
v-model-permission="['vf203', 'vf105', 'vf107', 'vf114', 'vf124', 'vf205']">WiFi</el-radio>
|
<el-radio :label="4" v-model-permission="['vf203']">4G</el-radio>
|
</el-radio-group>
|
</el-form-item>
|
</div>
|
<template v-if="net.type === 2">
|
<div class="config-form-item">
|
<el-form-item :label="$t('config.wifiName')">
|
<el-input v-model="net.ssid" type="text"
|
:placeholder="$t('config.msg_please_enter')"></el-input>
|
</el-form-item>
|
</div>
|
<div class="config-form-item">
|
<el-form-item :label="$t('config.wifiPassword')">
|
<el-input v-model="net.psk" type="text"
|
:placeholder="$t('config.msg_please_enter')"></el-input>
|
</el-form-item>
|
</div>
|
</template>
|
</el-form>
|
</div>
|
</el-card>
|
<el-card class="config-card card-zhanwei"></el-card>
|
<!-- IP配置卡片 -->
|
<el-card class="config-card card-theme-display">
|
<div class="config-card__header">
|
<i class="el-icon-s-platform config-card__header-icon"></i>
|
{{ $t('config.ipConfiguration') }}
|
</div>
|
<div class="config-card__body">
|
<el-form ref="ipForm" :model="net" :rules="netRules" label-width="140px">
|
<div class="config-form-item">
|
<el-form-item :label="$t('config.dhcpModeSelection')">
|
<el-radio-group v-model="net.dhcp">
|
<el-radio :label="1">{{ $t('config.customNetworkConfiguration') }}</el-radio>
|
<el-radio :label="2">{{ $t('config.dhcpMode') }}</el-radio>
|
</el-radio-group>
|
</el-form-item>
|
</div>
|
<template v-if="net.dhcp === 1">
|
<div class="config-form-item">
|
<el-form-item :label="$t('config.ipAddress')" prop="ip">
|
<el-input v-model="net.ip" placeholder="192.168.10.99"></el-input>
|
</el-form-item>
|
</div>
|
<div class="config-form-item">
|
<el-form-item :label="$t('config.gateway')" prop="gateway">
|
<el-input v-model="net.gateway" placeholder="192.168.1.1"></el-input>
|
</el-form-item>
|
</div>
|
<div class="config-form-item">
|
<el-form-item :label="$t('config.subnetMask')" prop="mask">
|
<el-input v-model="net.mask" placeholder="255.255.255.0"></el-input>
|
</el-form-item>
|
</div>
|
<div class="config-form-item">
|
<el-form-item :label="$t('config.dnsServer')" prop="dns">
|
<el-input v-model="net.dns" placeholder="8.8.8.8"></el-input>
|
</el-form-item>
|
</div>
|
</template>
|
</el-form>
|
</div>
|
</el-card>
|
<!-- 其他配置 -->
|
<el-card class="config-card card-theme-display">
|
<div class="config-card__header">
|
<i class="el-icon-s-platform config-card__header-icon"></i>
|
{{ $t('config.otherConfiguration') }}
|
</div>
|
<div class="config-card__body">
|
<el-form ref="ipForm" label-width="140px">
|
<div class="config-form-item">
|
<el-form-item :label="$t('config.mac')">
|
<el-input v-model="net.mac" placeholder="D6:18:6C:CE:B4:60" :disabled="true"></el-input>
|
</el-form-item>
|
</div>
|
</el-form>
|
</div>
|
</el-card>
|
<el-card class="config-card card-zhanwei"></el-card>
|
</div>
|
</el-tab-pane>
|
<!-- mqtt配置 -->
|
<el-tab-pane :label="$t('config.mqttRelatedConfiguration')" name="mqtt" v-if="!isWeCom">
|
<div class="config-grid">
|
<!-- mqtt信息配置 -->
|
<el-card class="config-card card-theme-display">
|
<div class="config-card__header">
|
<i class="el-icon-connection config-card__header-icon"></i>
|
{{ $t('config.mqttConnectionInformation') }}({{ ($t('config.enterpriseWechat')) }})
|
</div>
|
<div class="config-card__body">
|
<el-form ref="mqttForm" :model="mqtt" :rules="mqttRules" label-width="140px">
|
<!-- 服务器地址 -->
|
<div class="config-form-item">
|
<el-form-item :label="$t('config.serverAddress')" prop="addr">
|
<el-input v-model="mqtt.addr" :placeholder="$t('config.msg_please_enter')"></el-input>
|
</el-form-item>
|
</div>
|
<!-- 用户名 -->
|
<div class="config-form-item">
|
<el-form-item :label="$t('config.userName')">
|
<el-input v-model="mqtt.username" type="text"
|
:placeholder="$t('config.msg_please_enter')"></el-input>
|
</el-form-item>
|
</div>
|
<!-- 用户密码 -->
|
<div class="config-form-item">
|
<el-form-item :label="$t('config.userPassword')">
|
<el-input v-model="mqtt.password" type="text"
|
:placeholder="$t('config.msg_please_enter')"></el-input>
|
</el-form-item>
|
</div>
|
<!-- qos -->
|
<div class="config-form-item">
|
<el-form-item label="Qos">
|
<el-radio-group v-model="mqtt.qos">
|
<el-radio :label="0">Qos0</el-radio>
|
<el-radio :label="1">Qos1</el-radio>
|
<el-radio :label="2">Qos2</el-radio>
|
</el-radio-group>
|
</el-form-item>
|
</div>
|
<!-- 客户端Id -->
|
<div class="config-form-item">
|
<el-form-item :label="$t('config.clientID')">
|
<el-input v-model="mqtt.clientId" type="text" :placeholder="$t('config.msg_please_enter')"
|
disabled></el-input>
|
</el-form-item>
|
</div>
|
<!-- 是否加随机数 -->
|
<div class="config-form-item">
|
<el-form-item :label="$t('config.clientIdSuffix')">
|
<el-radio-group v-model="mqtt.clientIdSuffix">
|
<el-radio :label="0">{{ $t('config.no') }}</el-radio>
|
<el-radio :label="1">{{ $t('config.yes') }}</el-radio>
|
</el-radio-group>
|
</el-form-item>
|
</div>
|
<!-- 清除会话 -->
|
<!-- <div class="config-form-item">
|
<el-form-item :label="$t('config.cleanSession')">
|
<el-radio-group v-model="mqtt.cleanSession">
|
<el-radio :label="0">{{ $t('config.no') }}</el-radio>
|
<el-radio :label="1">{{ $t('config.yes') }}</el-radio>
|
</el-radio-group>
|
</el-form-item>
|
</div> -->
|
</el-form>
|
</div>
|
</el-card>
|
<!-- 会话配置 -->
|
<el-card class="config-card card-theme-display">
|
<div class="config-card__header">
|
<i class="el-icon-s-operation config-card__header-icon"></i>
|
{{ $t('config.sessionConfiguration') }}
|
</div>
|
<div class="config-card__body">
|
<el-form ref="onlineCheckingForm" :model="mqtt" :rules="mqttRules" label-width="140px">
|
<!-- 主题前缀 -->
|
<div class="config-form-item">
|
<el-form-item :label="$t('config.topicPrefix')">
|
<el-input v-model="mqtt.prefix" type="text"
|
:placeholder="$t('config.msg_please_enter')"></el-input>
|
</el-form-item>
|
</div>
|
<!-- 遗嘱 -->
|
<!-- <div class="config-form-item">
|
<el-form-item :label="$t('config.willTopic')">
|
<el-input v-model="mqtt.willTopic" type="text"
|
:placeholder="$t('config.msg_please_enter')"></el-input>
|
</el-form-item>
|
</div> -->
|
</el-form>
|
</div>
|
</el-card>
|
<!-- <el-card class="config-card card-zhanwei"></el-card> -->
|
<!-- 在线验证卡片 -->
|
<el-card class="config-card card-theme-display">
|
<div class="config-card__header">
|
<i class="el-icon-link config-card__header-icon"></i>
|
{{ $t('config.onlineChecking') }}
|
</div>
|
<div class="config-card__body">
|
<el-form ref="onlineCheckingForm" :model="mqtt" :rules="mqttRules" label-width="140px">
|
<!-- 在线验证 -->
|
<div class="config-form-item">
|
<el-form-item :label="$t('config.onlineChecking')">
|
<el-switch v-model="mqtt.onlinecheck" active-color="#1890ff" inactive-color="#ff4949"
|
:active-value="1" :inactive-value="0">
|
</el-switch>
|
</el-form-item>
|
</div>
|
<!-- 在线验证超时时间 -->
|
<div class="config-form-item">
|
<el-form-item :label="$t('config.onlineCheckingTimeout')" prop="timeout">
|
<el-input v-model="mqtt.timeout" :placeholder="$t('config.msg_please_enter')"></el-input><span>
|
{{ $t('config.second') }}</span>
|
</el-form-item>
|
</div>
|
</el-form>
|
</div>
|
</el-card>
|
</div>
|
</el-tab-pane>
|
<!-- 人脸配置 -->
|
<el-tab-pane :label="$t('config.faceRelatedConfiguration')" name="face">
|
<div class="config-grid">
|
<!-- mqtt信息配置 -->
|
<el-card class="config-card card-theme-display">
|
<div class="config-card__header">
|
<i class="el-icon-info config-card__header-icon"></i>
|
{{ $t('config.functionalInformation') }}
|
</div>
|
<div class="config-card__body">
|
<el-form ref="faceForm" :model="face" label-width="140px">
|
<!-- 人脸识别相似度 -->
|
<div class="config-form-item">
|
<el-form-item :label="$t('config.faceSimilarityThreshold')">
|
<div class="compact-controls">
|
<el-slider v-model="face.similarity" :max="100" :min="0" style="width:60%;"></el-slider>
|
<span class="value-display">{{ face.similarity }}%</span>
|
</div>
|
</el-form-item>
|
</div>
|
<!-- 活体检测 -->
|
<div class="config-form-item">
|
<el-form-item :label="$t('config.livenessDetectionFunction')">
|
<el-switch v-model="face.livenessOff" :active-value="1" :inactive-value="0"
|
active-color="#1890ff" inactive-color="#ff4949">
|
</el-switch>
|
</el-form-item>
|
</div>
|
<!-- 活体检测阈值 -->
|
<div class="config-form-item">
|
<el-form-item :label="$t('config.livenessDetectionThreshold')">
|
<div class="compact-controls">
|
<el-slider v-model="face.livenessVal" :max="100" :min="0" style="width:60%;"></el-slider>
|
<span class="value-display">{{ face.livenessVal }}</span>
|
</div>
|
</el-form-item>
|
</div>
|
<!-- 红外框开关 -->
|
<div class="config-form-item">
|
<el-form-item :label="$t('config.infraredImageDisplay')">
|
<el-switch v-model="face.showNir" :active-value="1" :inactive-value="0" active-color="#1890ff"
|
inactive-color="#ff4949">
|
</el-switch>
|
</el-form-item>
|
</div>
|
<!-- 口罩检测开关 -->
|
<!-- <div class="config-form-item">
|
<el-form-item :label="$t('config.maskRecognition')">
|
<el-switch v-model="face.detectMask" :active-value="1" :inactive-value="0"
|
active-color="#1890ff" inactive-color="#ff4949">
|
</el-switch>
|
</el-form-item>
|
</div> -->
|
<!-- 重检开关 -->
|
<div class="config-form-item">
|
<el-form-item :label="$t('config.recognitionSwitch')">
|
<el-switch v-model="face.recheck" :active-value="1" :inactive-value="0" active-color="#1890ff"
|
inactive-color="#ff4949">
|
</el-switch>
|
</el-form-item>
|
</div>
|
</el-form>
|
</div>
|
</el-card>
|
<el-card class="config-card card-zhanwei"></el-card>
|
<!-- 提示语 -->
|
<el-card class="config-card card-theme-display">
|
<div class="config-card__header">
|
<i class="el-icon-s-grid config-card__header-icon"></i>
|
{{ $t('config.prompt') }}
|
</div>
|
<div class="config-card__body">
|
<el-form ref="faceMsgForm" :model="face" :rules="faceRules" label-width="140px">
|
<!-- 未注册人脸提示 -->
|
<div class="config-form-item">
|
<el-form-item :label="$t('config.strangerVoice')">
|
<el-radio-group v-model="face.stranger">
|
<el-radio :label="0">{{ $t('config.noVoice') }}</el-radio>
|
<el-radio :label="1">{{ $t('config.broadcastPleaseRegisterFirst') }}</el-radio>
|
<el-radio :label="2">{{ $t('config.broadcastHelloStranger') }}</el-radio>
|
</el-radio-group>
|
</el-form-item>
|
</div>
|
<!-- 识别成功播放语音 -->
|
<div class="config-form-item">
|
<el-form-item :label="$t('config.voiceMode')">
|
<el-radio-group v-model="face.voiceMode">
|
<el-radio :label="0">{{ $t('config.noVoice') }}</el-radio>
|
<el-radio :label="1">{{ $t('config.broadcastName') }}</el-radio>
|
<el-radio :label="2">{{ $t('config.broadcastGreeting') }}</el-radio>
|
</el-radio-group>
|
</el-form-item>
|
</div>
|
<!-- 自定义欢迎语 -->
|
<div class="config-form-item" v-if="face.voiceMode == 2">
|
<el-form-item :label="$t('config.voiceModeDate')">
|
<el-input v-model="face.voiceModeDate" :placeholder="$t('config.msg_please_enter')"></el-input>
|
</el-form-item>
|
</div>
|
</el-form>
|
</div>
|
</el-card>
|
</div>
|
</el-tab-pane>
|
<!-- 系统配置 -->
|
<el-tab-pane :label="$t('config.systemRelatedConfiguration')" name="sys">
|
<div class="config-grid">
|
<!-- 功能开关配置 -->
|
<el-card class="config-card card-theme-display">
|
<div class="config-card__header">
|
<i class="el-icon-video-play config-card__header-icon"></i>
|
{{ $t('config.functionSwitch') }}
|
</div>
|
<div class="config-card__body">
|
<el-form ref="sysForm" :model="sys" label-width="140px">
|
<!-- 刷卡核验 -->
|
<div class="config-form-item">
|
<el-form-item :label="$t('config.cardSwipingSwitch')">
|
<el-switch v-model="sys.nfc" :active-value="1" :inactive-value="0" active-color="#1890ff"
|
inactive-color="#ff4949">
|
</el-switch>
|
</el-form-item>
|
</div>
|
<!-- 密码开门 -->
|
<div class="config-form-item">
|
<el-form-item :label="$t('config.passwordSwitch')">
|
<el-switch v-model="sys.pwd" :active-value="1" :inactive-value="0" active-color="#1890ff"
|
inactive-color="#ff4949">
|
</el-switch>
|
</el-form-item>
|
</div>
|
<!-- 陌生人保存图片 -->
|
<div class="config-form-item">
|
<el-form-item :label="$t('config.strangerImage')">
|
<el-radio-group v-model="sys.strangerImage">
|
<el-radio :label="0">{{ $t('config.notsave') }}</el-radio>
|
<el-radio :label="1">{{ $t('config.save') }}</el-radio>
|
</el-radio-group>
|
</el-form-item>
|
</div>
|
<!-- 云证开关 -->
|
<div class="config-form-item">
|
<el-form-item :label="$t('config.cloudCertificateSwitch')">
|
<el-switch v-model="sys.nfcIdentityCardEnable" :active-value="3" :inactive-value="1"
|
active-color="#1890ff" inactive-color="#ff4949">
|
</el-switch>
|
</el-form-item>
|
</div>
|
</el-form>
|
</div>
|
</el-card>
|
<!-- 心跳设置 -->
|
<el-card class="config-card card-theme-display">
|
<div class="config-card__header">
|
<i class="el-icon-setting config-card__header-icon"></i>
|
{{ $t('config.heartbeatConfig') }}
|
</div>
|
<div class="config-card__body">
|
<el-form ref="sysForm" :model="sys" label-width="140px">
|
<!-- 心跳开关 -->
|
<div class="config-form-item">
|
<el-form-item :label="$t('config.heartbeatSwitch')">
|
<el-switch v-model="sys.heart_en" :active-value="1" :inactive-value="0" active-color="#1890ff"
|
inactive-color="#ff4949">
|
</el-switch>
|
</el-form-item>
|
</div>
|
<!-- 心跳间隔 -->
|
<div class="config-form-item">
|
<el-form-item :label="$t('config.heartRateInterval')" prop="heart_time">
|
<el-input v-model="sys.heart_time" :placeholder="$t('config.msg_please_enter')" type="number"
|
:min="1"></el-input><span>
|
{{ $t('config.second') }}</span>
|
</el-form-item>
|
</div>
|
<!-- 心跳主题 -->
|
<!-- <div class="config-form-item">
|
<el-form-item :label="$t('config.heartbeatTopic')" prop="heart_topic">
|
<el-input v-model="sys.heart_topic" :placeholder="$t('config.msg_please_enter')"></el-input>
|
</el-form-item>
|
</div> -->
|
<!-- 心跳内容 -->
|
<!-- <div class="config-form-item">
|
<el-form-item :label="$t('config.heartbeatContent')" prop="heart_payload">
|
<el-input v-model="sys.heart_payload" :placeholder="$t('config.msg_please_enter')"></el-input>
|
</el-form-item>
|
</div> -->
|
</el-form>
|
</div>
|
</el-card>
|
<!-- 扫码设置 -->
|
<el-card class="config-card card-theme-display" v-model-permission="['vf105', 'vf114']">
|
<div class="config-card__header">
|
<i class="el-icon-full-screen config-card__header-icon"></i>
|
{{ $t('config.scanSettings') }}
|
</div>
|
<div class="config-card__body">
|
<el-form ref="sysForm" :model="sys" label-width="140px">
|
<!-- 扫码开关 -->
|
<!-- <div class="config-form-item">
|
<el-form-item :label="$t('config.scanSwitch')">
|
<el-switch v-model="sys.scan" :active-value="1" :inactive-value="0" active-color="#1890ff"
|
inactive-color="#ff4949">
|
</el-switch>
|
</el-form-item>
|
</div> -->
|
<!-- 扫码间隔 -->
|
<div class="config-form-item">
|
<el-form-item :label="$t('config.scanInterval')" prop="scanInterval">
|
<el-input v-model="sys.scanInterval"
|
:placeholder="$t('config.msg_please_enter')"></el-input><span>
|
{{ $t('config.second') }}</span>
|
</el-form-item>
|
</div>
|
</el-form>
|
</div>
|
</el-card>
|
<!-- 基本信息 -->
|
<el-card class="config-card card-theme-display">
|
<div class="config-card__header">
|
<i class="el-icon-info config-card__header-icon"></i>
|
{{ $t('config.basicInformation') }}
|
</div>
|
<div class="config-card__body">
|
<el-form ref="accessForm" :model="sys" label-width="140px">
|
<div class="config-form-item">
|
<el-form-item :label="$t('config.deviceMac')">
|
<el-input v-model="sys.mac" :placeholder="$t('config.msg_please_enter')" disabled></el-input>
|
</el-form-item>
|
</div>
|
<div class="config-form-item">
|
<el-form-item :label="$t('config.uuid')">
|
<el-input v-model="sys.uuid" :placeholder="$t('config.msg_please_enter')" disabled></el-input>
|
</el-form-item>
|
</div>
|
<div class="config-form-item">
|
<el-form-item :label="$t('config.sn')">
|
<el-input v-model="sys.sn" :placeholder="$t('config.msg_please_enter')" disabled></el-input>
|
</el-form-item>
|
</div>
|
<div class="config-form-item">
|
<el-form-item :label="$t('config.model')">
|
<el-input v-model="sys.model" :placeholder="$t('config.msg_please_enter')" disabled></el-input>
|
</el-form-item>
|
</div>
|
<div class="config-form-item">
|
<el-form-item :label="$t('config.version')">
|
<el-input v-model="sys.version" :placeholder="$t('config.msg_please_enter')"
|
disabled></el-input>
|
</el-form-item>
|
</div>
|
<div class="config-form-item">
|
<el-form-item :label="$t('config.appVersion')">
|
<el-input v-model="sys.appVersion" :placeholder="$t('config.msg_please_enter')"
|
disabled></el-input>
|
</el-form-item>
|
</div>
|
<div class="config-form-item">
|
<el-form-item :label="$t('config.releaseTime')">
|
<el-input v-model="sys.releaseTime" :placeholder="$t('config.msg_please_enter')"
|
disabled></el-input>
|
</el-form-item>
|
</div>
|
<div class="config-form-item">
|
<el-form-item :label="$t('config.totaldisk')">
|
<el-input v-model="sys.totaldisk" :placeholder="$t('config.msg_please_enter')"
|
disabled></el-input>
|
</el-form-item>
|
</div>
|
<div class="config-form-item">
|
<el-form-item :label="$t('config.freedisk')">
|
<el-input v-model="sys.freedisk" :placeholder="$t('config.msg_please_enter')"
|
disabled></el-input>
|
</el-form-item>
|
</div>
|
</el-form>
|
</div>
|
</el-card>
|
</div>
|
</el-tab-pane>
|
<!-- 通行配置 -->
|
<el-tab-pane :label="$t('config.passageConfiguration')" name="access">
|
<div class="config-grid">
|
<el-card class="config-card card-theme-display">
|
<div class="config-card__header">
|
<i class="el-icon-connection config-card__header-icon"></i>
|
{{ $t('config.functionConfiguration') }}
|
</div>
|
<div class="config-card__body">
|
<el-form ref="accessForm" :model="access" :rules="accessRules" label-width="140px">
|
<div class="config-form-item">
|
<el-form-item :label="$t('config.numberOfPassageRecords')">
|
<el-input v-model="access.offlineAccessNum"
|
:placeholder="$t('config.msg_please_enter')"></el-input>
|
</el-form-item>
|
</div>
|
<div class="config-form-item">
|
<el-form-item :label="$t('config.durationOfRelayOpening')">
|
<el-input v-model="access.relayTime" :placeholder="$t('config.msg_please_enter')"></el-input>
|
<span> {{ $t('config.second') }}</span>
|
</el-form-item>
|
</div>
|
<div class="config-form-item">
|
<el-form-item :label="$t('config.fireAlarmSwitch')">
|
<el-switch v-model="access.fire" :active-value="1" :inactive-value="0" active-color="#1890ff"
|
inactive-color="#ff4949">
|
</el-switch>
|
</el-form-item>
|
</div>
|
<div class="config-form-item">
|
<el-form-item :label="$t('config.fireAlarmStatus')">
|
<el-radio-group v-model="access.fireStatus">
|
<el-radio :label="0">{{ $t('config.normal') }}</el-radio>
|
<el-radio :label="1">{{ $t('config.warning') }}</el-radio>
|
</el-radio-group>
|
</el-form-item>
|
</div>
|
<div class="config-form-item">
|
<el-form-item :label="$t('config.tamperSwitch')">
|
<el-switch v-model="access.tamper" :active-value="1" :inactive-value="0" active-color="#1890ff"
|
inactive-color="#ff4949">
|
</el-switch>
|
</el-form-item>
|
</div>
|
<div class="config-form-item">
|
<el-form-item :label="$t('config.uploadToCloudSwitch')">
|
<el-switch v-model="access.uploadToCloud" :active-value="1" :inactive-value="0"
|
active-color="#1890ff" inactive-color="#ff4949">
|
</el-switch>
|
</el-form-item>
|
</div>
|
</el-form>
|
</div>
|
</el-card>
|
</div>
|
</el-tab-pane>
|
<!-- 时钟配置 -->
|
<el-tab-pane :label="$t('config.clockConfiguration')" name="ntp">
|
<div class="config-grid">
|
<el-card class="config-card card-theme-display">
|
<div class="config-card__header">
|
<i class="el-icon-connection config-card__header-icon"></i>
|
{{ $t('config.clockConfiguration') }}({{ $t('config.restartAfterSetting') }})
|
</div>
|
<div class="config-card__body">
|
<el-form ref="ntpForm" :model="ntp" :rules="ntpRules" label-width="140px">
|
<div class="config-form-item">
|
<el-form-item :label="$t('config.timeSynchronizationServerIP')" prop="server">
|
<el-input v-model="ntp.server" :placeholder="$t('config.msg_please_enter')"></el-input>
|
</el-form-item>
|
</div>
|
<div class="config-form-item">
|
<el-form-item :label="$t('config.timeZone')" prop="gmt">
|
<div class="timezone-selects">
|
<el-select v-model="selectedTimezoneRegion" :placeholder="$t('config.msg_please_enter')"
|
@change="handleRegionChange">
|
<el-option v-for="region in timezoneRegions" :key="region.value" :label="region.label"
|
:value="region.value"></el-option>
|
</el-select>
|
<el-select v-model="selectedTimezoneCity" :placeholder="$t('config.msg_please_enter')"
|
@change="handleCityChange">
|
<el-option v-for="city in timezoneCities" :key="city.value" :label="city.label"
|
:value="city.value"></el-option>
|
</el-select>
|
</div>
|
</el-form-item>
|
</div>
|
</el-form>
|
</div>
|
</el-card>
|
</div>
|
</el-tab-pane>
|
<!-- 云证激活 -->
|
<el-tab-pane :label="$t('config.cloudCertificateActivation')" name="cloud">
|
<div class="config-grid">
|
<el-card class="config-card card-theme-display">
|
<div class="config-card__header cloud-card-header">
|
<i class="el-icon-cloudy config-card__header-icon cloud-icon"></i>
|
{{ $t('config.cloudCertificateActivation') }}
|
<!-- <div class="cloud-status" :class="cloudStatusClass">
|
{{ cloudStatusText }}
|
</div> -->
|
</div>
|
<div class="config-card__body">
|
<el-form ref="cloudForm" :model="cloudForm" :rules="cloudRules" label-position="top">
|
<div class="activation-container">
|
<!-- 激活密钥输入区域 -->
|
<div class="activation-input-section">
|
<el-form-item :label="$t('config.activationKey')" prop="activationKey"
|
class="activation-form-item">
|
<el-input v-model="cloudForm.activationKey" type="textarea"
|
:placeholder="$t('config.msg_please_enter')" class="activation-input" clearable>
|
<template #prefix>
|
<i class="el-icon-key"></i>
|
</template>
|
</el-input>
|
<!-- 输入提示 -->
|
<div class="activation-tips">
|
<div class="tip-item">
|
<i class="el-icon-info"></i>
|
<span>{{ $t('config.cloudTips1') }}</span>
|
</div>
|
<div class="tip-item">
|
<i class="el-icon-warning-outline"></i>
|
<span>{{ $t('config.cloudTips2') }}</span>
|
</div>
|
</div>
|
</el-form-item>
|
</div>
|
<!-- 激活按钮区域 -->
|
<div class="activation-action-section">
|
<el-button type="primary" class="activation-btn" :loading="activating"
|
:disabled="!cloudForm.activationKey" @click="handleActivate">
|
<i class="el-icon-check"></i>
|
{{ activating ? $t('config.activationInProgress') : $t('config.confirmActivation') }}
|
</el-button>
|
</div>
|
<!-- 激活状态显示 -->
|
<!-- <div v-if="activationResult" class="activation-result" :class="activationResultClass">
|
<i :class="resultIcon"></i>
|
<span>{{ activationResult.message }}</span>
|
<span v-if="activationResult.details" class="result-details">{{ activationResult.details
|
}}</span>
|
</div> -->
|
</div>
|
</el-form>
|
</div>
|
</el-card>
|
</div>
|
</el-tab-pane>
|
<!-- 密码配置 -->
|
<el-tab-pane :label="$t('config.passwordModification')" name="pwd">
|
<div class="config-grid">
|
<el-card class="config-card card-theme-display">
|
<div class="config-card__header">
|
<i class="el-icon-connection config-card__header-icon"></i>
|
{{ $t('config.password') }}
|
</div>
|
<div class="config-card__body">
|
<el-form ref="pwdForm" :model="pwd" :rules="pwdRules" label-width="140px">
|
<div class="config-form-item">
|
<el-form-item :label="$t('config.oldPassword')" prop="oldPwd">
|
<el-input style="border:1px solid #eee; width:370px;border-radius: 10px;" v-model="pwd.oldPwd"
|
autocomplete="off"></el-input>
|
</el-form-item>
|
</div>
|
<div class="config-form-item" style="clear: both;">
|
<el-form-item :label="$t('config.newPassword')" prop="newPwd">
|
<el-input style="border:1px solid #eee; width:370px;border-radius: 10px;" v-model="pwd.newPwd"
|
autocomplete="off"></el-input>
|
</el-form-item>
|
</div>
|
<div class="config-form-item">
|
<el-form-item :label="$t('config.confirmPassword')" prop="quePwd">
|
<el-input style="border:1px solid #eee; width:370px;border-radius: 10px;" v-model="pwd.quePwd"
|
autocomplete="off"></el-input>
|
</el-form-item>
|
</div>
|
</el-form>
|
<!-- 密码规则提示语 -->
|
<div class="password-rules-tip">
|
<div class="rules-title">{{ $t('config.passwordRule') }}:</div>
|
<ul class="rules-list">
|
<li>{{ $t('config.passwordLength') }}</li>
|
<li>{{ $t('config.cannotBeTheSame') }}</li>
|
<li>{{ $t('config.cannotOrder') }}</li>
|
<li>{{ $t('config.cannotWeakPassword') }}:</li>
|
<li class="weak-passwords">"password", "admin", "qwerty", "iloveyou"<br>
|
"letmein", "welcome", "sunshine", "monkey"</li>
|
</ul>
|
</div>
|
</div>
|
</el-card>
|
</div>
|
</el-tab-pane>
|
<!-- 资源配置 -->
|
<el-tab-pane :label="$t('config.resourceConfiguration')" name="resource">
|
<div class="config-grid">
|
<el-card class="config-card card-theme-display">
|
<div class="config-card__header">
|
<i class="el-icon-picture-outline config-card__header-icon"></i>
|
{{ $t('config.backgroundImage') }}
|
</div>
|
<div class="config-card__body">
|
<div class="background-upload-section">
|
<div class="upload-area">
|
<el-upload class="background-upload" action="#" :show-file-list="false" accept="image/png"
|
:before-upload="handleBackgroundSelect">
|
<el-button type="primary" icon="el-icon-folder-opened">
|
{{ $t('config.selectImage') }}
|
</el-button>
|
</el-upload>
|
<div v-if="background.name" class="file-info">
|
<i class="el-icon-document"></i>
|
<span>{{ background.name }}</span>
|
</div>
|
</div>
|
<div class="upload-actions">
|
<el-button type="primary" icon="el-icon-upload" :disabled="!background.base64"
|
:loading="loading.background" @click="uploadBackground">
|
{{ loading.background ? $t('config.uploading') : $t('config.uploadBackground') }}
|
</el-button>
|
</div>
|
<div class="upload-tips">
|
<p v-if="model == 'vf202'">{{ $t('config.backgroundUploadTip').replace('{n}', '480*854') }}</p>
|
<p v-if="model == 'vf105' || model == 'vf107' || model == 'vf205'">{{
|
$t('config.backgroundUploadTip').replace('{n}', '800*1280') }}</p>
|
<p v-if="model == 'vf114' || model == 'vf124'">{{ $t('config.backgroundUploadTip').replace('{n}',
|
'720*1280') }}</p>
|
<p v-if="model == 'vf203'">{{ $t('config.backgroundUploadTip').replace('{n}', '600*1024') }}</p>
|
</div>
|
</div>
|
</div>
|
</el-card>
|
</div>
|
</el-tab-pane>
|
</el-tabs>
|
</div>
|
<el-button v-if="activeTab != 'cloud' && activeTab != 'resource'" class="config-save-btn" type="primary"
|
icon="el-icon-check" size="large" @click="saveAll">{{
|
$t('config.saveConfig') }}</el-button>
|
</div>
|
</div>
|
</template>
|
<script>
|
import { throttle } from '@/utils/index.js'
|
import { timezones, timezoneRegionNames } from '@/utils/timezones.js'
|
export default {
|
data() {
|
// 密码验证规则
|
const validatePassword = (rule, value, callback) => {
|
if (!value || value.trim() === '') {
|
callback(new Error(this.$t('config.msg_inputPassword')));
|
} else {
|
callback();
|
}
|
};
|
// 确认密码验证规则(必须与新密码一致)
|
const validateConfirmPassword = (rule, value, callback) => {
|
if (value !== this.pwd.newPwd) {
|
callback(new Error(this.$t('config.msg_password_mismatch')));
|
} else {
|
callback();
|
}
|
};
|
return {
|
activeTab: 'base',
|
isWeCom: false,
|
base: {
|
screenOff: 0,
|
screensaver: 0,
|
brightness: 70,
|
// brightnessAuto: 1,
|
volume: 50,
|
showIp: 1,
|
showSn: 1,
|
// showProgramCode: 1,
|
// showIdentityCard: 1,
|
language: 'CN',
|
appMode: 1,
|
// nirBrightness: 80,
|
backlight: 70,
|
// firstLogin: 0
|
},
|
idleData: [this.$t('config.never'), this.$t('config.min1'), this.$t('config.min2'), this.$t('config.min3'), this.$t('config.min4'), this.$t('config.min5')],
|
baseRules: {
|
screenOff: [{
|
message: this.$t("common.integerFormat"),
|
trigger: ["blur"],
|
pattern: /^[0-9]+$/
|
}],
|
screensaver: [{
|
message: this.$t("common.integerFormat"),
|
trigger: ["blur"],
|
pattern: /^[0-9]+$/
|
}]
|
},
|
net: {
|
type: 1,
|
ssid: '',
|
psk: '',
|
dhcp: 2,
|
ip: '',
|
gateway: '',
|
mask: '',
|
dns: '',
|
mac: ''
|
},
|
netRules: {
|
ip: [
|
{
|
message: this.$t("common.incorrectFormat"),
|
trigger: ["blur"],
|
pattern: /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/
|
}
|
],
|
gateway: [
|
{
|
message: this.$t("common.incorrectFormat"),
|
trigger: ["blur"],
|
pattern: /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/
|
}
|
],
|
mask: [
|
{
|
message: this.$t("common.incorrectFormat"),
|
trigger: ["blur"],
|
pattern: /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/
|
}
|
],
|
dns: [
|
{
|
message: this.$t("common.incorrectFormat"),
|
trigger: ["blur"],
|
pattern: /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/
|
}
|
]
|
},
|
mqtt: {
|
addr: '',
|
username: '',
|
password: '',
|
prefix: '',
|
qos: 0,
|
onlinecheck: '',
|
timeout: '',
|
// cleanSession: 0,
|
clientId: '',
|
clientIdSuffix: '',
|
// willTopic: '',
|
},
|
mqttRules: {
|
addr: [
|
{
|
validator: (rule, value, callback) => {
|
const supportedProtocols = ['tcp://', 'ssl://', 'mqtt://', 'mqtts://'];
|
const ipOrDomainWithPort = /^(?:(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)|(?:[a-zA-Z0-9-]+\.)+[a-zA-Z]{2,})(:\d{1,5})?$/
|
let hostPort = value;
|
let hasSupportedProtocol = false;
|
for (const protocol of supportedProtocols) {
|
if (value.startsWith(protocol)) {
|
hasSupportedProtocol = true;
|
hostPort = value.slice(protocol.length); // 去掉协议前缀
|
break;
|
}
|
}
|
if (!hasSupportedProtocol && value.includes('://')) callback(new Error(this.$t("common.incorrectFormat")));
|
if (!hostPort) {
|
callback();
|
return;
|
}
|
if (!ipOrDomainWithPort.test(hostPort)) callback(new Error(this.$t("common.incorrectFormat")));
|
const portMatch = hostPort.match(/:(\d{1,5})$/);
|
if (portMatch) {
|
const port = parseInt(portMatch[1], 10);
|
if (port < 1 || port > 65535) callback(new Error(this.$t("common.incorrectFormat")));
|
}
|
callback();
|
},
|
trigger: ["blur"]
|
}],
|
timeout: [
|
{
|
message: this.$t("common.incorrectFormat"),
|
trigger: ["blur"],
|
pattern: /^[1-9]\d*$/
|
}
|
],
|
},
|
face: {
|
similarity: 60,
|
livenessOff: 1,
|
livenessVal: 0,
|
showNir: 0,
|
stranger: 0,
|
voiceMode: 0,
|
voiceModeDate: "",
|
recheck: 0
|
},
|
faceRules: {
|
// voiceModeDate: [
|
// {
|
// message: this.$t("config.msg_charactersAndNumbers"),
|
// trigger: ["blur"],
|
// pattern: /^[\u4e00-\u9fa5]*([1-9]\d*)?[\u4e00-\u9fa5]*$/
|
// }
|
// ],
|
},
|
sys: {
|
nfc: 1,
|
pwd: 1,
|
strangerImage: 1,
|
heart_en: 0,
|
heart_time: 30,
|
// heart_topic: '',
|
// heart_payload: '',
|
mac: '',
|
uuid: '',
|
sn: '',
|
model: '',
|
version: '',
|
appVersion: '',
|
releaseTime: '',
|
nfcIdentityCardEnable: 1,
|
// scan: '',
|
scanInterval: '',
|
totaldisk: '',
|
freedisk: '',
|
},
|
sysRules: {
|
heart_time: [{
|
message: this.$t("common.positiveIntegerFormat"),
|
trigger: ["blur"],
|
pattern: /^[1-9]\d*$/
|
}],
|
scanInterval: [
|
{
|
message: this.$t("common.incorrectFormat"),
|
trigger: ["blur"],
|
pattern: /^[1-9]\d*$/
|
}
|
],
|
},
|
access: {
|
offlineAccessNum: 2000,
|
relayTime: 2000,
|
fire: 0,
|
fireStatus: 0,
|
tamper: 0,
|
uploadToCloud: 0,
|
},
|
accessRules: {},
|
ntp: {
|
server: '182.92.12.11',
|
gmt: 'Asia/Shanghai'
|
},
|
selectedTimezoneRegion: '',
|
selectedTimezoneCity: '',
|
ntpRules: {
|
ip: [
|
{
|
message: this.$t("common.incorrectFormat"),
|
trigger: ["blur"],
|
pattern: /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/
|
}
|
],
|
gmt: [
|
{
|
required: true,
|
message: this.$t("config.msg_please_enter"),
|
trigger: ["change"]
|
}
|
],
|
},
|
pwd: {
|
orignPwd: '',
|
oldPwd: '',
|
newPwd: '',
|
quePwd: ''
|
},
|
pwdRules: {
|
oldPwd: [
|
{
|
required: true,
|
validator: validatePassword,
|
trigger: ["blur"]
|
}
|
],
|
newPwd: [
|
{ required: true, validator: validatePassword, trigger: ["blur"] },
|
],
|
quePwd: [
|
{ required: true, validator: validatePassword, trigger: ["blur"] },
|
{ validator: validateConfirmPassword, trigger: ["blur"] }
|
]
|
},
|
passwordStrength: 0, // 密码强度等级 0-4
|
isWeakPassword: false,
|
cloudForm: {
|
activationKey: ''
|
},
|
cloudRules: {
|
activationKey: [
|
{ required: true, message: '请输入激活密钥', trigger: 'blur' },
|
{
|
message: this.$t("common.incorrectFormat"),
|
trigger: ["blur"],
|
pattern: /^___VBAR_ID_ACTIVE_V/
|
}
|
]
|
},
|
activating: false,
|
activationResult: null,
|
cloudStatus: 'inactive', // 'active' 或 'inactive'
|
background: {
|
base64: '',
|
name: ''
|
},
|
loading: {
|
background: false
|
},
|
reboot: {
|
language: ''
|
},
|
originalBase: null, // 保存从服务器获取的原始base配置
|
version: 0, // 版本:1-国际版本,0-国内版本
|
model: ''
|
}
|
},
|
created() {
|
// 从sessionStorage获取version,默认为0(国内版本)
|
let publicConfig = sessionStorage.getItem('publicConfig')
|
let { version, isWeCom, model } = publicConfig ? JSON.parse(publicConfig) : {}
|
if (version == null || version == undefined || version == "CN") {
|
this.version = 0
|
} else {
|
this.version = 1
|
}
|
this.isWeCom = isWeCom
|
this.model = model
|
this.getConfig()
|
},
|
watch: {
|
'ntp.gmt': function () {
|
this.initTimezone()
|
}
|
},
|
computed: {
|
activeConfig() {
|
let config = this[this.activeTab] || {};
|
if (this.activeTab == 'base') {
|
if (this.reboot.language == config.language) {
|
config = { ...config }
|
delete config.language
|
}
|
}
|
if (this.activeTab == 'face') {
|
config.similarity = config.similarity / 100
|
config.livenessVal = config.livenessVal / 10
|
}
|
if (this.activeTab == 'pwd') {
|
config = {
|
password: config.newPwd
|
}
|
}
|
if (this.activeTab == 'ntp') {
|
const { gmt, ...rest } = config
|
config = {
|
...rest,
|
timeZone: gmt
|
}
|
}
|
if (this.activeTab == 'sys') {
|
let { nfc, pwd, strangerImage, heart_en, heart_time, nfcIdentityCardEnable, scanInterval } = config
|
config = { nfc, pwd, strangerImage, heart_en, heart_time, nfcIdentityCardEnable }
|
if (scanInterval) {
|
config.scanInterval = scanInterval
|
}
|
}
|
if (this.activeTab == 'mqtt') {
|
config = { ...config }
|
delete config.clientId
|
}
|
if (this.activeTab == 'net') {
|
delete config.mac
|
}
|
return config
|
},
|
cloudStatusClass() {
|
return this.cloudStatus === 'active' ? 'active' : 'inactive'
|
},
|
cloudStatusText() {
|
return this.cloudStatus === 'active' ? '已激活' : '未激活'
|
},
|
activationResultClass() {
|
return this.activationResult?.type || 'success'
|
},
|
resultIcon() {
|
return this.activationResult?.type === 'error' ? 'el-icon-close' : 'el-icon-check'
|
},
|
timezoneRegions() {
|
const regions = new Set()
|
Object.keys(timezones).forEach(key => {
|
const [region] = key.split('/')
|
regions.add(region)
|
})
|
const locale = this.$i18n.locale || 'en'
|
return Array.from(regions).map(r => ({ label: this.getRegionLabel(r, locale), value: r }))
|
},
|
timezoneCities() {
|
return this.getCitiesByRegion(this.selectedTimezoneRegion || 'Asia')
|
},
|
},
|
methods: {
|
initTimezone() {
|
const current = this.ntp.gmt && typeof this.ntp.gmt === 'string' ? this.ntp.gmt : 'Asia/Shanghai'
|
const parts = current.split('/')
|
const region = parts[0]
|
const city = parts.slice(1).join('/')
|
this.selectedTimezoneRegion = region || 'Asia'
|
const cities = this.getCitiesByRegion(this.selectedTimezoneRegion)
|
const defaultCity = city && cities.find(c => c.value === city) ? city : (cities[0] && cities[0].value)
|
this.selectedTimezoneCity = defaultCity || ''
|
if (this.selectedTimezoneRegion && this.selectedTimezoneCity) {
|
this.ntp.gmt = `${this.selectedTimezoneRegion}/${this.selectedTimezoneCity}`
|
}
|
},
|
getCitiesByRegion(region) {
|
const locale = this.$i18n.locale || 'en'
|
return Object.keys(timezones)
|
.filter(key => key.startsWith(region + '/'))
|
.map(key => {
|
const city = key.split('/')[1]
|
const tz = timezones[key]
|
const label = (tz.name && (tz.name[locale] || tz.name.en)) || city
|
return { value: city, label }
|
})
|
},
|
getRegionLabel(region) {
|
const locale = this.$i18n.locale || 'en'
|
const map = timezoneRegionNames[locale] || timezoneRegionNames.en || {}
|
return map[region] || region
|
},
|
handleRegionChange(region) {
|
const cities = this.getCitiesByRegion(region)
|
if (cities.length) {
|
this.selectedTimezoneCity = cities[0].value
|
this.ntp.gmt = `${region}/${this.selectedTimezoneCity}`
|
} else {
|
this.selectedTimezoneCity = ''
|
this.ntp.gmt = ''
|
}
|
},
|
handleCityChange(city) {
|
if (this.selectedTimezoneRegion && city) {
|
this.ntp.gmt = `${this.selectedTimezoneRegion}/${city}`
|
}
|
},
|
handleActivate() {
|
this.$refs['cloudForm'].validate(async (valid) => {
|
if (valid) {
|
let data = {
|
code: this.cloudForm.activationKey
|
}
|
try {
|
const res = await this.$http.post('/eidActive', { data })
|
if (res.code == 200) {
|
this.$message.success(this.$t('config.activationSuccessful'))
|
this.cloudForm.activationKey = ''
|
} else {
|
this.$message.error(res.message)
|
}
|
} catch (error) {
|
console.log(error)
|
}
|
}
|
})
|
},
|
handleClick() {
|
if (this.activeTab !== 'pwd') {
|
this.getConfig()
|
this.pwd = {
|
orignPwd: '',
|
oldPwd: '',
|
newPwd: '',
|
quePwd: ''
|
}
|
}
|
if (this.activeTab !== 'cloud' && this.cloudForm.activationKey) {
|
this.cloudForm.activationKey = ''
|
}
|
if (this.$refs.displayForm) {
|
this.$refs.displayForm.clearValidate();
|
}
|
if (this.$refs.infoForm) {
|
this.$refs.infoForm.clearValidate();
|
}
|
if (this.$refs.audioForm) {
|
this.$refs.audioForm.clearValidate();
|
}
|
if (this.$refs.systemForm) {
|
this.$refs.systemForm.clearValidate();
|
}
|
if (this.$refs.networkForm) {
|
this.$refs.networkForm.clearValidate();
|
}
|
if (this.$refs.ipForm) {
|
this.$refs.ipForm.clearValidate();
|
}
|
if (this.$refs.mqttForm) {
|
this.$refs.mqttForm.clearValidate();
|
}
|
if (this.$refs.onlineCheckingForm) {
|
this.$refs.onlineCheckingForm.clearValidate();
|
}
|
if (this.$refs.faceForm) {
|
this.$refs.faceForm.clearValidate();
|
}
|
if (this.$refs.faceMsgForm) {
|
this.$refs.faceMsgForm.clearValidate();
|
}
|
if (this.$refs.sysForm) {
|
this.$refs.sysForm.clearValidate();
|
}
|
if (this.$refs.accessForm) {
|
this.$refs.accessForm.clearValidate();
|
}
|
if (this.$refs.ntpForm) {
|
this.$refs.ntpForm.clearValidate();
|
}
|
if (this.$refs.pwdForm) {
|
this.$refs.pwdForm.clearValidate();
|
}
|
if (this.$refs.cloudForm) {
|
this.$refs.cloudForm.clearValidate();
|
}
|
},
|
async getConfig() {
|
const res = await this.$http.post(
|
"/getConfig",
|
{
|
data: ""
|
}
|
);
|
console.log(res)
|
if (res.code == 200) {
|
let { base, mqtt, sys, face, ntp, access, net } = res.data || {}
|
if (base) {
|
this.base = this.mergeWithBase(this.base, base);
|
// 保存原始base配置,用于后续比较
|
this.originalBase = { ...base };
|
this.pwd.orignPwd = base.password
|
this.reboot.language = base.language
|
}
|
if (mqtt) {
|
this.mqtt = this.mergeWithBase(this.mqtt, mqtt);
|
}
|
if (sys) {
|
sys.totaldisk = sys.totaldisk + ' M'
|
sys.freedisk = sys.freedisk + ' M'
|
this.sys = this.mergeWithBase(this.sys, sys);
|
}
|
if (face) {
|
face.similarity = Math.round(face.similarity * 100)
|
face.livenessVal = face.livenessVal * 10
|
this.face = this.mergeWithBase(this.face, face);
|
}
|
if (ntp) {
|
const mergedNtp = this.mergeWithBase(this.ntp, ntp)
|
if (ntp.timeZone) {
|
mergedNtp.gmt = ntp.timeZone
|
}
|
this.ntp = mergedNtp
|
this.initTimezone()
|
} else {
|
this.initTimezone()
|
}
|
if (access) this.access = this.mergeWithBase(this.access, access);
|
if (net) this.net = this.mergeWithBase(this.net, net);
|
} else {
|
this.$message.error(res.message)
|
}
|
},
|
saveAll: throttle(async function () {
|
try {
|
// 分别验证每个表单
|
if (this.activeTab == 'base') {
|
await this.$refs.displayForm.validate();
|
}
|
if (this.activeTab == 'pwd') {
|
await this.$refs.pwdForm.validate();
|
console.log(this.pwd.orignPwd, this.pwd.oldPwd)
|
if (this.pwd.orignPwd != this.pwd.oldPwd) {
|
this.$message.error(this.$t('config.msg_oldPasswordError'));
|
return
|
}
|
}
|
if (this.activeTab == 'face') {
|
await this.$refs.faceMsgForm.validate();
|
}
|
await this.submitData();
|
} catch (error) {
|
console.log(error)
|
}
|
}, 2000),
|
async submitData() {
|
const res = await this.$http.post(
|
"/setConfig",
|
{
|
data: {
|
[this.activeTab == 'pwd' ? 'base' : this.activeTab]: this.activeConfig
|
}
|
}
|
);
|
if (res.code == 200) {
|
this.$message.success(this.$t('config.msg_saveSuccess'))
|
if (this.activeTab == 'pwd') {
|
this.pwd = {
|
orignPwd: '',
|
oldPwd: '',
|
newPwd: '',
|
quePwd: ''
|
}
|
}
|
this.getConfig()
|
} else {
|
this.$message.error(res.message)
|
}
|
},
|
// 只合并目标对象中存在的属性
|
mergeWithBase(target, source) {
|
if (!source || typeof source !== 'object') {
|
return { ...target };
|
}
|
const result = { ...target };
|
const targetKeys = Object.keys(target);
|
for (let i = 0; i < targetKeys.length; i++) {
|
const key = targetKeys[i];
|
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
result[key] = source[key];
|
}
|
}
|
return result;
|
},
|
// 处理背景图片选择
|
async handleBackgroundSelect(file) {
|
if (file.type !== 'image/png') {
|
this.$message.error(this.$t('config.backgroundImageOnlyPNG'));
|
return false;
|
}
|
if (file.size > 5 * 1024 * 1024) {
|
this.$message.error(this.$t('config.backgroundSizeLimit'));
|
return false;
|
}
|
const targetResolution = this.getBackgroundResolutionByModel();
|
if (targetResolution) {
|
try {
|
const { width, height } = await this.getImageSize(file);
|
if (width !== targetResolution.width || height !== targetResolution.height) {
|
this.$message.error(this.$t('config.backgroundResolutionMismatch').replace('{n}', `${targetResolution.width}*${targetResolution.height}`));
|
return false;
|
}
|
} catch (error) {
|
this.$message.error(this.$t('config.backgroundParseFailed'));
|
return false;
|
}
|
}
|
try {
|
const base64 = await this.readFileAsBase64(file);
|
this.background = {
|
base64,
|
name: file.name
|
};
|
this.$message.success(this.$t('config.backgroundImageSelected'));
|
} catch (error) {
|
this.$message.error(this.$t('config.backgroundParseFailed'));
|
}
|
return false;
|
},
|
getBackgroundResolutionByModel() {
|
const resolutionMap = {
|
vf202: { width: 480, height: 854 },
|
vf105: { width: 800, height: 1280 },
|
vf107: { width: 800, height: 1280 },
|
vf205: { width: 800, height: 1280 },
|
vf114: { width: 720, height: 1280 },
|
vf124: { width: 720, height: 1280 },
|
vf203: { width: 600, height: 1024 }
|
};
|
return resolutionMap[this.model] || null;
|
},
|
getImageSize(file) {
|
return new Promise((resolve, reject) => {
|
const image = new Image();
|
const imageUrl = URL.createObjectURL(file);
|
image.onload = () => {
|
URL.revokeObjectURL(imageUrl);
|
resolve({ width: image.width, height: image.height });
|
};
|
image.onerror = () => {
|
URL.revokeObjectURL(imageUrl);
|
reject(new Error('image read failed'));
|
};
|
image.src = imageUrl;
|
});
|
},
|
// 读取文件为Base64
|
readFileAsBase64(file) {
|
return new Promise((resolve, reject) => {
|
const reader = new FileReader();
|
reader.onload = () => {
|
const result = reader.result || '';
|
const base64 = typeof result === 'string' ? result.split(',').pop() : '';
|
if (!base64) {
|
reject(new Error('empty result'));
|
return;
|
}
|
resolve(base64);
|
};
|
reader.onerror = () => reject(new Error('read failed'));
|
reader.readAsDataURL(file);
|
});
|
},
|
// 上传背景图片
|
async uploadBackground() {
|
if (!this.background.base64) {
|
this.$message.warning(this.$t('config.backgroundRequired'));
|
return;
|
}
|
this.loading.background = true;
|
try {
|
const res = await this.$http.post("/control", { data: { command: 13, extra: { wallpaperBase64: this.background.base64 } } });
|
if (res.code == 200) {
|
this.$message.success(this.$t('config.backgroundSuccess'));
|
this.background = {
|
base64: '',
|
name: ''
|
};
|
} else {
|
this.$message.error(res.message || this.$t('config.backgroundFailed'));
|
}
|
} catch (error) {
|
this.$message.error(this.$t('config.backgroundFailed'));
|
} finally {
|
this.loading.background = false;
|
}
|
},
|
}
|
}
|
</script>
|
<style lang="less" scoped>
|
.config-container {
|
padding: 20px;
|
background-color: #f5f7fa;
|
min-height: 100vh;
|
max-width: 100vw;
|
/* 限制最大宽度为视口宽度 */
|
overflow-x: hidden;
|
/* 隐藏横向溢出 */
|
}
|
.config-grid {
|
column-count: 2;
|
/* 两列 */
|
column-gap: 20px;
|
/* 列间距 */
|
margin-bottom: 80px;
|
box-sizing: border-box;
|
width: 100%;
|
}
|
.config-card {
|
break-inside: avoid;
|
/* 防止卡片跨列分割 */
|
display: inline-block;
|
/* 关键属性 */
|
width: 100%;
|
/* 占满列宽 */
|
margin-bottom: 20px;
|
/* 卡片间距 */
|
border-radius: 12px;
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
background: white;
|
transition: transform 0.3s ease;
|
vertical-align: top;
|
/* 顶部对齐 */
|
}
|
.config-card:hover {
|
transform: translateY(-2px);
|
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.12);
|
}
|
.config-card__header {
|
background: linear-gradient(90deg, #ecf5ff, #f0f9ff);
|
border-bottom: 1px solid #ebeef5;
|
padding: 16px 20px;
|
border-top-left-radius: 12px;
|
border-top-right-radius: 12px;
|
font-weight: 600;
|
color: #409EFF;
|
display: flex;
|
align-items: center;
|
}
|
.config-card__header-icon {
|
margin-right: 8px;
|
font-size: 18px;
|
}
|
.config-card__body {
|
padding: 20px;
|
display: flex;
|
flex-direction: column;
|
}
|
/* 确保所有卡片内容自然高度 */
|
.card-content {
|
display: flex;
|
flex-direction: column;
|
height: auto !important;
|
}
|
.config-form-item {
|
display: flex;
|
align-items: center;
|
margin-bottom: 8px;
|
padding-bottom: 8px;
|
border-bottom: 1px dashed #ebeef5;
|
}
|
/* 确保el-form-item的label对齐 */
|
.config-card__body ::v-deep .el-form-item {
|
display: flex;
|
align-items: center;
|
width: 100%;
|
margin-bottom: 0;
|
}
|
.config-card__body ::v-deep .el-form-item__label {
|
// width: 140px;
|
text-align: right;
|
padding-right: 15px;
|
color: #606266;
|
font-weight: 500;
|
flex-shrink: 0;
|
}
|
.config-card__body ::v-deep .el-form-item__content {
|
flex: 1;
|
margin-left: 0 !important;
|
}
|
/* 语言选项每行3个布局 */
|
.language-radio-group {
|
display: flex;
|
flex-wrap: wrap;
|
width: 100%;
|
}
|
.language-radio-group ::v-deep .el-radio {
|
width: calc(33.333% - 10px);
|
margin-right: 15px;
|
margin-left: 0;
|
}
|
.language-radio-group ::v-deep .el-radio:nth-child(3n) {
|
margin-right: 0;
|
}
|
.config-form-item:last-child {
|
border-bottom: none;
|
margin-bottom: 0;
|
padding-bottom: 0;
|
}
|
.config-form-control {
|
flex: 1;
|
}
|
.config-save-btn {
|
position: fixed;
|
bottom: 30px;
|
right: 30px;
|
z-index: 100;
|
box-shadow: 0 4px 12px rgba(32, 160, 255, 0.4);
|
}
|
.config-tabs {
|
background: transparent;
|
}
|
.config-tabs ::v-deep .el-tabs__header {
|
background: #fff;
|
border-radius: 8px;
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
margin-bottom: 20px;
|
border: none;
|
}
|
.config-tabs ::v-deep .el-tabs__item {
|
font-weight: 500;
|
height: 50px;
|
line-height: 50px;
|
}
|
.compact-controls {
|
display: flex;
|
align-items: center;
|
gap: 15px;
|
flex-wrap: wrap;
|
}
|
.value-display {
|
min-width: 40px;
|
text-align: center;
|
color: #409EFF;
|
font-weight: 500;
|
}
|
.divider {
|
height: 1px;
|
background: linear-gradient(90deg, transparent, #dcdfe6, transparent);
|
margin: 20px 0;
|
}
|
/* 响应式设计 */
|
@media (max-width: 1200px) {
|
.config-grid {
|
grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
|
}
|
}
|
@media (max-width: 768px) {
|
.config-container {
|
padding: 10px;
|
}
|
.config-grid {
|
grid-template-columns: 1fr;
|
gap: 15px;
|
}
|
.config-form-item {
|
flex-direction: column;
|
align-items: flex-start;
|
gap: 8px;
|
}
|
.config-form-control {
|
width: 100%;
|
}
|
.compact-controls {
|
width: 100%;
|
justify-content: space-between;
|
}
|
/* 小屏幕下语言选项每行2个 */
|
.language-radio-group ::v-deep .el-radio {
|
width: calc(50% - 8px);
|
}
|
.language-radio-group ::v-deep .el-radio:nth-child(3n) {
|
margin-right: 15px;
|
}
|
.language-radio-group ::v-deep .el-radio:nth-child(2n) {
|
margin-right: 0;
|
}
|
}
|
@media (max-width: 480px) {
|
.config-card__body {
|
padding: 15px;
|
}
|
.config-form-item {
|
margin-bottom: 15px;
|
padding-bottom: 15px;
|
}
|
/* 超小屏幕下语言选项每行1个 */
|
.language-radio-group ::v-deep .el-radio {
|
width: 100%;
|
margin-right: 0;
|
}
|
.language-radio-group ::v-deep .el-radio:nth-child(2n) {
|
margin-right: 0;
|
}
|
}
|
/* 卡片颜色主题 */
|
.card-theme-display {
|
--card-header-bg: linear-gradient(90deg, #ecf5ff, #f0f9ff);
|
--card-header-color: #409EFF;
|
}
|
.card-theme-network {
|
--card-header-bg: linear-gradient(90deg, #f0f9eb, #f9fbe7);
|
--card-header-color: #67C23A;
|
}
|
.card-theme-audio {
|
--card-header-bg: linear-gradient(90deg, #fef0f0, #fef6f6);
|
--card-header-color: #F56C6C;
|
}
|
.card-theme-system {
|
--card-header-bg: linear-gradient(90deg, #f4f4f5, #f9f9fa);
|
--card-header-color: #909399;
|
}
|
.card-theme-security {
|
--card-header-bg: linear-gradient(90deg, #fdf6ec, #fefcef);
|
--card-header-color: #E6A23C;
|
}
|
.card-theme-display .config-card__header {
|
background: var(--card-header-bg);
|
color: var(--card-header-color);
|
}
|
.card-theme-network .config-card__header {
|
background: var(--card-header-bg);
|
color: var(--card-header-color);
|
}
|
.card-theme-audio .config-card__header {
|
background: var(--card-header-bg);
|
color: var(--card-header-color);
|
}
|
.card-theme-system .config-card__header {
|
background: var(--card-header-bg);
|
color: var(--card-header-color);
|
}
|
.card-theme-security .config-card__header {
|
background: var(--card-header-bg);
|
color: var(--card-header-color);
|
}
|
.card-zhanwei {
|
height: 0;
|
border: none;
|
}
|
/deep/.el-form-item__label {
|
text-align: center;
|
display: flex;
|
justify-content: center;
|
align-items: center;
|
}
|
/deep/.el-form-item__content>.el-input {
|
width: 60%;
|
}
|
.password-strength-container {
|
display: flex;
|
align-items: center;
|
justify-content: space-between;
|
margin-top: 2px;
|
width: 400px;
|
}
|
.password-strength-bar {
|
width: 90%;
|
height: 6px;
|
background-color: #f0f0f0;
|
border-radius: 3px;
|
overflow: hidden;
|
margin-bottom: 4px;
|
}
|
.password-strength-progress {
|
height: 100%;
|
transition: all 0.3s ease;
|
width: 0%;
|
}
|
/* 修正选择器 - 直接应用于进度条元素 */
|
.password-strength-progress.strength-weak {
|
width: 25%;
|
background-color: #ff4d4f;
|
}
|
.password-strength-progress.strength-medium {
|
width: 50%;
|
background-color: #faad14;
|
}
|
.password-strength-progress.strength-good {
|
width: 75%;
|
background-color: #1890ff;
|
}
|
.password-strength-progress.strength-strong {
|
width: 100%;
|
background-color: #52c41a;
|
}
|
.password-strength-text {
|
line-height: 20px;
|
}
|
/* 修正选择器 - 直接应用于文本元素 */
|
.password-strength-text.strength-weak {
|
color: #ff4d4f;
|
}
|
.password-strength-text.strength-medium {
|
color: #faad14;
|
}
|
.password-strength-text.strength-good {
|
color: #1890ff;
|
}
|
.password-strength-text.strength-strong {
|
color: #52c41a;
|
}
|
.password-weak-warning {
|
font-size: 12px;
|
color: #faad14;
|
margin-top: 4px;
|
display: flex;
|
align-items: center;
|
}
|
.password-weak-warning i {
|
margin-right: 4px;
|
}
|
.password-rules-tip {
|
margin-top: 20px;
|
padding: 12px 16px;
|
background: #f8f9fa;
|
border: 1px solid #e9ecef;
|
border-radius: 8px;
|
font-size: 12px;
|
line-height: 1.5;
|
color: #495057;
|
}
|
.rules-title {
|
font-weight: 600;
|
color: #343a40;
|
margin-bottom: 8px;
|
font-size: 13px;
|
}
|
.rules-list {
|
margin: 0;
|
padding-left: 16px;
|
list-style: none;
|
}
|
.rules-list li {
|
margin-bottom: 4px;
|
position: relative;
|
}
|
.rules-list li:before {
|
content: "·";
|
position: absolute;
|
left: -12px;
|
color: #6c757d;
|
}
|
.weak-passwords {
|
color: #dc3545;
|
font-style: italic;
|
margin-top: 4px;
|
line-height: 1.4;
|
background: rgba(220, 53, 69, 0.05);
|
padding: 6px 8px;
|
border-radius: 4px;
|
border-left: 3px solid #dc3545;
|
}
|
/* 云证激活页面样式 */
|
.cloud-card-header {
|
display: flex;
|
align-items: center;
|
position: relative;
|
}
|
.cloud-icon {
|
color: #409EFF;
|
font-size: 18px;
|
}
|
.cloud-status {
|
margin-left: auto;
|
padding: 4px 12px;
|
border-radius: 12px;
|
font-size: 12px;
|
font-weight: 500;
|
}
|
.cloud-status.active {
|
background: #f0f9ff;
|
color: #1890ff;
|
border: 1px solid #91d5ff;
|
}
|
.cloud-status.inactive {
|
background: #fff2f0;
|
color: #ff4d4f;
|
border: 1px solid #ffccc7;
|
}
|
.activation-container {
|
max-width: 600px;
|
margin: 0 auto;
|
}
|
.activation-form-item {
|
margin-bottom: 24px;
|
}
|
.activation-form-item .el-form-item__label {
|
font-weight: 600;
|
color: #303133;
|
font-size: 14px;
|
margin-bottom: 8px;
|
padding: 0;
|
}
|
.activation-input {
|
width: 100% !important;
|
}
|
/deep/.activation-input .el-textarea__inner {
|
height: 100px !important;
|
border-radius: 8px;
|
border: 2px solid #e6e8eb;
|
font-size: 14px;
|
transition: all 0.3s ease;
|
padding-left: 10px;
|
}
|
.activation-input .el-textarea__inner:focus {
|
border-color: #409EFF;
|
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.1);
|
}
|
.activation-input .el-input__prefix {
|
display: flex;
|
align-items: center;
|
left: 12px;
|
color: #909399;
|
}
|
.activation-tips {
|
margin-top: 12px;
|
padding: 16px;
|
background: #f8f9fa;
|
border-radius: 8px;
|
border-left: 4px solid #409EFF;
|
}
|
.tip-item {
|
display: flex;
|
align-items: flex-start;
|
margin-bottom: 8px;
|
font-size: 13px;
|
color: #606266;
|
line-height: 1.4;
|
}
|
.tip-item:last-child {
|
margin-bottom: 0;
|
}
|
.tip-item i {
|
margin-right: 8px;
|
margin-top: 2px;
|
color: #409EFF;
|
font-size: 14px;
|
}
|
.activation-action-section {
|
text-align: center;
|
padding: 24px 0;
|
border-top: 1px solid #f0f0f0;
|
border-bottom: 1px solid #f0f0f0;
|
margin: 20px 0;
|
}
|
.background-upload-section {
|
padding: 20px 0;
|
}
|
.upload-area {
|
margin-bottom: 20px;
|
}
|
.background-upload {
|
display: inline-block;
|
}
|
.file-info {
|
margin-top: 12px;
|
padding: 8px 12px;
|
background: #f5f7fa;
|
border-radius: 6px;
|
display: flex;
|
align-items: center;
|
gap: 8px;
|
color: #606266;
|
font-size: 13px;
|
}
|
.file-info i {
|
color: #409EFF;
|
}
|
.upload-actions {
|
margin-bottom: 16px;
|
}
|
.upload-tips {
|
padding: 12px;
|
background: #f0f9ff;
|
border-radius: 6px;
|
border-left: 3px solid #409EFF;
|
}
|
.upload-tips p {
|
margin: 0;
|
color: #606266;
|
font-size: 13px;
|
line-height: 1.5;
|
}
|
.activation-btn {
|
height: 44px;
|
padding: 0 32px;
|
font-size: 14px;
|
font-weight: 600;
|
border-radius: 8px;
|
background: linear-gradient(135deg, #409EFF, #66b1ff);
|
border: none;
|
box-shadow: 0 2px 8px rgba(64, 158, 255, 0.3);
|
transition: all 0.3s ease;
|
}
|
.activation-btn:hover:not(.is-disabled) {
|
transform: translateY(-2px);
|
box-shadow: 0 4px 12px rgba(64, 158, 255, 0.4);
|
}
|
.activation-btn:active:not(.is-disabled) {
|
transform: translateY(0);
|
}
|
.action-helpers {
|
margin-top: 16px;
|
display: flex;
|
justify-content: center;
|
gap: 16px;
|
}
|
.helper-btn {
|
color: #606266;
|
font-size: 13px;
|
transition: color 0.3s ease;
|
}
|
.helper-btn:hover {
|
color: #409EFF;
|
}
|
.activation-result {
|
padding: 16px;
|
border-radius: 8px;
|
margin-top: 20px;
|
display: flex;
|
align-items: center;
|
flex-direction: column;
|
text-align: center;
|
animation: fadeIn 0.5s ease;
|
}
|
.activation-result.success {
|
background: #f6ffed;
|
border: 1px solid #b7eb8f;
|
color: #52c41a;
|
}
|
.activation-result.error {
|
background: #fff2f0;
|
border: 1px solid #ffccc7;
|
color: #ff4d4f;
|
}
|
.activation-result i {
|
font-size: 24px;
|
margin-bottom: 8px;
|
}
|
.result-details {
|
margin-top: 8px;
|
font-size: 12px;
|
color: #8c8c8c;
|
line-height: 1.4;
|
}
|
.timezone-selects {
|
display: flex;
|
gap: 12px;
|
}
|
.timezone-selects .el-select {
|
flex: 1;
|
}
|
@keyframes fadeIn {
|
from {
|
opacity: 0;
|
transform: translateY(-10px);
|
}
|
to {
|
opacity: 1;
|
transform: translateY(0);
|
}
|
}
|
/* 响应式设计 */
|
@media (max-width: 768px) {
|
.activation-container {
|
max-width: 100%;
|
}
|
.action-helpers {
|
flex-direction: column;
|
gap: 8px;
|
}
|
.activation-btn {
|
width: 100%;
|
max-width: 280px;
|
}
|
}
|
</style>
|