<template>
|
<div id="device-control-panel">
|
<!-- 标签页 -->
|
<el-tabs v-model="activeTab" type="border-card" class="main-tabs" @tab-click="handleClick">
|
<!-- 远程控制标签 -->
|
<el-tab-pane :label="$t('control.remoteControl')" name="remote">
|
<div class="remote-control">
|
<el-row :gutter="20">
|
<!-- 重启设备 -->
|
<el-col :span="8">
|
<div class="control-card restart-card" @click="handleRestart" :class="{ 'loading': loading.restart }">
|
<div class="card-content">
|
<div class="card-icon">
|
<i class="el-icon-refresh"></i>
|
</div>
|
<h3>{{ $t('control.restart') }}</h3>
|
<p class="card-description">{{ $t('control.restartTips') }}</p>
|
<div class="card-hover-effect">
|
<span>{{ $t('control.clickToRestart') }}</span>
|
</div>
|
<div v-if="loading.restart" class="loading-overlay">
|
<i class="fas fa-spinner"></i>
|
<div class="loading-text">{{ $t('control.restarting') }}</div>
|
</div>
|
</div>
|
</div>
|
</el-col>
|
|
<!-- 远程开门 -->
|
<el-col :span="8">
|
<div class="control-card open-card" @click="handleRemoteOpen" :class="{ 'loading': loading.remoteOpen }">
|
<div class="card-content">
|
<div class="card-icon">
|
<i class="el-icon-unlock"></i>
|
</div>
|
<h3>{{ $t('control.remoteOpen') }}</h3>
|
<p class="card-description">{{ $t('control.remoteTips') }}</p>
|
<div class="card-hover-effect">
|
<span>{{ $t('control.clickToOpen') }}</span>
|
</div>
|
<div v-if="loading.remoteOpen" class="loading-overlay">
|
<i class="fas fa-spinner"></i>
|
<div class="loading-text">{{ $t('control.opening') }}</div>
|
</div>
|
</div>
|
</div>
|
</el-col>
|
|
<!-- 设备重置 -->
|
<el-col :span="8">
|
<div class="control-card reset-card" @click="handleReset" :class="{ 'loading': loading.reset }">
|
<div class="card-content">
|
<div class="card-icon">
|
<i class="el-icon-delete"></i>
|
</div>
|
<h3>{{ $t('control.reset') }}</h3>
|
<p class="card-description">{{ $t('control.resetTips') }}</p>
|
<div class="card-hover-effect">
|
<span>{{ $t('control.clickToReset') }}</span>
|
</div>
|
<div v-if="loading.reset" class="loading-overlay">
|
<i class="fas fa-spinner"></i>
|
<div class="loading-text">{{ $t('control.reseting') }}</div>
|
</div>
|
</div>
|
</div>
|
</el-col>
|
</el-row>
|
</div>
|
</el-tab-pane>
|
|
<!-- 设备升级标签 -->
|
<el-tab-pane :label="$t('control.firmwareUpgrade')" name="upgrade">
|
<div class="firmware-upgrade">
|
<el-row :gutter="30">
|
<el-col :span="12">
|
<el-card class="upgrade-card" shadow="never">
|
<h3><i class="fas fa-cloud-download-alt"></i>{{ $t('control.urlUpgrade') }}</h3>
|
<el-form ref="upgradeForm" :model="upgradeForm" :rules="upgradeRules" label-position="top"
|
class="upgrade-form">
|
|
<el-form-item :label="$t('control.firmwareUrl')" prop="url">
|
<el-input v-model="upgradeForm.url" placeholder="https://example.com/firmware.dpk" clearable>
|
<i slot="prefix" class="el-input__icon el-icon-link"></i>
|
</el-input>
|
</el-form-item>
|
|
<el-form-item :label="$t('control.md5Checksum')" prop="md5">
|
<el-input v-model="upgradeForm.md5" :placeholder="$t('common.placeholder')" clearable>
|
<i slot="prefix" class="el-input__icon el-icon-key"></i>
|
</el-input>
|
</el-form-item>
|
|
<el-form-item>
|
<el-button type="primary" @click="submitUpgrade" :loading="loading.upgrade" style="width: 100%">
|
{{ $t('control.startUpgrade') }}
|
</el-button>
|
</el-form-item>
|
</el-form>
|
</el-card>
|
</el-col>
|
<el-col :span="12">
|
<el-card class="upgrade-card" shadow="never">
|
<h3><i class="fas fa-info-circle"></i> {{ $t('control.fileUpgrade') }}</h3>
|
<div class="upload-area" @click="triggerFileUpload">
|
<div class="upload-icon">
|
<i class="el-icon-upload"></i>
|
</div>
|
<div class="upload-text">{{ $t('control.uploadFile') }}</div>
|
<div class="upload-hint">{{ $t('control.formatFile') }}</div>
|
<input type="file" ref="fileInput" style="display: none" accept=".zip,.dpk"
|
@change="handleFileUpload">
|
</div>
|
<el-form>
|
<el-form-item :label="$t('control.md5Checksum')">
|
<el-input v-model="fileMd5" :placeholder="$t('common.placeholder')" clearable>
|
<i slot="prefix" class="el-input__icon el-icon-key"></i>
|
</el-input>
|
</el-form-item>
|
</el-form>
|
<div v-if="uploadedFile" class="file-info">
|
<div class="file-info-item">
|
<div class="file-label">{{ $t('control.fileName') }}:</div>
|
<div class="file-value">{{ uploadedFile.name }}</div>
|
</div>
|
<div class="file-info-item">
|
<div class="file-label">{{ $t('control.size') }}:</div>
|
<div class="file-value">{{ formatFileSize(uploadedFile.size) }}</div>
|
</div>
|
<!-- <div class="file-info-item">
|
<div class="file-label">类型:</div>
|
<div class="file-value">{{ uploadedFile.type || '未知类型' }}</div>
|
</div>
|
<div class="file-info-item">
|
<div class="file-label">MD5:</div>
|
<div class="file-value">{{ fileMd5 || '计算中...' }}</div>
|
</div>
|
<div class="file-info-item">
|
<div class="file-label">URL:</div>
|
<div class="file-value">{{ fileUrl || '待生成...' }}</div>
|
</div> -->
|
</div>
|
|
<div v-if="uploadedFile" class="upload-actions">
|
<el-button @click="clearUpload">{{ $t('control.clearFile') }}</el-button>
|
<el-button type="primary" @click="submitFileUpgrade" :loading="loading.fileUpgrade">
|
{{ loading.fileUpgrade ? $t('control.uploading') : $t('control.uploadAndUpgrade') }}
|
</el-button>
|
</div>
|
</el-card>
|
</el-col>
|
</el-row>
|
</div>
|
</el-tab-pane>
|
</el-tabs>
|
|
<!-- 重置确认对话框 -->
|
<el-dialog :title="$t('control.resetConfirm')" :visible.sync="resetDialogVisible" width="30%"
|
:close-on-click-modal="false" custom-class="reset-dialog">
|
|
<div class="reset-warning">
|
<el-alert :title="$t('control.resetWarningTitle')" type="warning" :description="$t('control.resetWarningDesc')"
|
show-icon>
|
</el-alert>
|
|
<el-form ref="resetForm" :model="resetForm" :rules="resetRules" style="margin-top: 20px">
|
<el-form-item :label="$t('control.resetPassword')" prop="password">
|
<el-input v-model="resetForm.password" type="password" show-password
|
:placeholder="$t('control.passwordPlaceholder')">
|
</el-input>
|
</el-form-item>
|
</el-form>
|
</div>
|
|
<span slot="footer" class="dialog-footer">
|
<el-button @click="resetDialogVisible = false">{{ $t('basicSetting.cancelButton') }}</el-button>
|
<el-button type="danger" @click="confirmReset" :loading="loading.reset">{{ $t('control.confirmResetBtn')
|
}}</el-button>
|
</span>
|
</el-dialog>
|
</div>
|
</template>
|
|
<script>
|
import { resetObjectValues, throttle } from '@/utils/index.js'
|
export default {
|
name: 'Control',
|
data () {
|
return {
|
activeTab: 'remote',
|
// 表单数据
|
upgradeForm: {
|
type: 0,
|
url: '',
|
md5: ''
|
},
|
|
fileMd5: '',
|
|
resetForm: {
|
password: ''
|
},
|
|
// 对话框状态
|
resetDialogVisible: false,
|
|
// 加载状态
|
loading: {
|
restart: false,
|
remoteOpen: false,
|
upgrade: false,
|
reset: false,
|
fileUpgrade: false,
|
},
|
|
// 表单验证规则
|
upgradeRules: {
|
url: [
|
{ required: true, message: this.$t('control.urlRequired'), trigger: 'blur' },
|
{ type: 'url', message: this.$t('control.urlInvalid'), trigger: 'blur' }
|
],
|
md5: [
|
{ required: true, message: this.$t('control.md5Required'), trigger: 'blur' },
|
{ pattern: /^[a-fA-F0-9]{32}$/, message: this.$t('control.md5Invalid'), trigger: 'blur' }
|
],
|
password: [
|
{ required: true, message: this.$t('control.passwordRequired'), trigger: 'blur' }
|
]
|
},
|
|
resetRules: {
|
password: [
|
{ required: true, message: this.$t('control.passwordRequired'), trigger: 'blur' }
|
]
|
},
|
|
uploadedFile: ''
|
}
|
},
|
|
methods: {
|
// 处理重启
|
handleRestart () {
|
this.$confirm(
|
this.$t('control.restartConfirm'),
|
this.$t('control.restart'),
|
{
|
confirmButtonText: this.$t('common.confirm'),
|
cancelButtonText: this.$t('common.cancel'),
|
type: 'warning'
|
}
|
).then(async () => {
|
this.loading.restart = true;
|
const res = await this.$http.post("/control", { data: { command: 0 } });
|
if (res.code == 200) {
|
this.loading.restart = false;
|
this.$message.success(this.$t('control.restartSuccess'));
|
} else {
|
this.loading.restart = false;
|
this.$message.success(this.$t('control.restartFailed'));
|
}
|
}).catch(() => {
|
// 取消操作
|
this.loading.restart = false;
|
});
|
},
|
|
// 处理远程开门
|
handleRemoteOpen () {
|
this.$confirm(
|
this.$t('control.openConfirm'),
|
this.$t('control.remoteOpen'),
|
{
|
confirmButtonText: this.$t('common.confirm'),
|
cancelButtonText: this.$t('common.cancel'),
|
type: 'warning'
|
}
|
).then(async () => {
|
this.loading.remoteOpen = true;
|
const res = await this.$http.post("/control", { data: { command: 1 } });
|
if (res.code == 200) {
|
this.loading.remoteOpen = false;
|
this.$message.success(this.$t('control.remoteOpenSuccess'));
|
} else {
|
this.loading.remoteOpen = false;
|
this.$message.success(this.$t('control.remoteOpenFailed'));
|
}
|
}).catch(() => {
|
// 取消操作
|
this.loading.remoteOpen = false
|
});
|
},
|
|
// 处理设备重置
|
handleReset () {
|
let that = this
|
this.$confirm(
|
this.$t('control.resetConfirm') + this.$t('control.resetWillOut'),
|
this.$t('control.reset'),
|
{
|
confirmButtonText: this.$t('common.confirm'),
|
cancelButtonText: this.$t('common.cancel'),
|
type: 'warning'
|
}
|
).then(async () => {
|
this.loading.reset = true;
|
const res = await this.$http.post("/control", { data: { command: 4 } });
|
if (res.code == 200) {
|
this.loading.reset = false;
|
this.$message.success(this.$t('control.resetSuccess'));
|
setTimeout(() => {
|
that.$router.push('/login')
|
sessionStorage.removeItem("token")
|
sessionStorage.removeItem("publicConfig")
|
}, 1000)
|
} else {
|
this.loading.reset = false;
|
this.$message.success(this.$t('control.resetFailed'));
|
}
|
}).catch(() => {
|
// 取消操作
|
this.loading.reset = false;
|
});
|
},
|
|
handleClick () {
|
if (this.$refs.upgradeForm) {
|
this.$refs.upgradeForm.clearValidate();
|
}
|
},
|
|
// 提交升级
|
submitUpgrade: throttle(function () {
|
let that = this
|
this.$refs.upgradeForm.validate((valid) => {
|
if (valid) {
|
this.$confirm(
|
this.$t('control.upgradeConfirm'),
|
this.$t('control.firmwareUpgrade'),
|
{
|
confirmButtonText: this.$t('common.confirm'),
|
cancelButtonText: this.$t('common.cancel'),
|
type: 'primary'
|
}
|
).then(async () => {
|
that.loading.upgrade = true;
|
const res = await that.$http.post("/upgradeFirmware", { data: that.upgradeForm });
|
if (res.code == 200) {
|
that.loading.upgrade = false;
|
that.$message.success(that.$t('control.upgradeSuccess'));
|
resetObjectValues(that.upgradeForm)
|
} else {
|
that.$message.error(res.message)
|
}
|
}).catch(() => {
|
// 取消操作
|
that.loading.upgrade = false;
|
});
|
}
|
});
|
}, 2000),
|
|
confirmReset () { },
|
|
triggerFileUpload () {
|
this.$refs.fileInput.click();
|
},
|
|
handleFileUpload (event) {
|
const file = event.target.files[0];
|
if (!file) return;
|
|
// 检查文件类型
|
const validTypes = ['.zip', '.dpk'];
|
const fileExtension = '.' + file.name.split('.').pop().toLowerCase();
|
|
if (!validTypes.includes(fileExtension)) {
|
this.$message.error(this.$t('control.formatFile'));
|
return;
|
}
|
|
// 检查文件大小 (500MB)
|
if (file.size > 20 * 1024 * 1024) {
|
this.$message.error(this.$t('control.formatFile'));
|
return;
|
}
|
this.uploadedFile = file;
|
},
|
|
formatFileSize (bytes) {
|
if (bytes === 0) return '0 Bytes';
|
const k = 1024;
|
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
},
|
|
clearUpload () {
|
this.uploadedFile = null;
|
this.fileMd5 = '';
|
this.fileUrl = '';
|
this.$refs.fileInput.value = '';
|
},
|
|
async submitFileUpgrade () {
|
if (!this.uploadedFile) {
|
this.$message.warning('请先选择要上传的文件');
|
return;
|
}
|
if (!this.fileMd5) {
|
this.$message.warning(this.$t('control.md5Required'));
|
return;
|
}
|
this.loading.fileUpgrade = true;
|
let formData = new FormData()
|
// 添加文件
|
formData.append('file', this.uploadedFile)
|
formData.append('md5', this.fileMd5)
|
const res = await this.$http.post("/upload", formData);
|
if (res.code == 200) {
|
this.loading.fileUpgrade = false;
|
this.$message.success(this.$t('control.upgradeSuccess'));
|
this.uploadedFile = null
|
this.fileMd5 = ''
|
} else {
|
this.$message.error(res.message)
|
this.loading.fileUpgrade = false;
|
}
|
|
}
|
}
|
}
|
</script>
|
|
<style scoped>
|
#device-control-panel {
|
padding: 20px;
|
max-width: 100vw;
|
}
|
|
.device-status {
|
display: flex;
|
align-items: center;
|
background: rgba(16, 16, 48, 0.7);
|
padding: 10px 20px;
|
border-radius: 30px;
|
border: 1px solid rgba(24, 144, 255, 0.3);
|
box-shadow: 0 0 15px rgba(24, 144, 255, 0.2);
|
}
|
|
.status-indicator {
|
width: 12px;
|
height: 12px;
|
border-radius: 50%;
|
margin-right: 10px;
|
background: var(--themeColor);
|
box-shadow: 0 0 10px var(--themeColor);
|
animation: pulse 2s infinite;
|
}
|
|
@keyframes pulse {
|
0% {
|
box-shadow: 0 0 0 0 rgba(24, 144, 255, 0.7);
|
}
|
|
70% {
|
box-shadow: 0 0 0 10px rgba(64, 224, 208, 0);
|
}
|
|
100% {
|
box-shadow: 0 0 0 0 rgba(64, 224, 208, 0);
|
}
|
}
|
|
.main-tabs {
|
background: rgba(16, 16, 48, 0.7);
|
border-radius: 15px;
|
border: 1px solid rgba(24, 144, 255, 0.2);
|
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
|
overflow: hidden;
|
}
|
|
.main-tabs::before {
|
content: '';
|
position: absolute;
|
top: 0;
|
left: 0;
|
width: 100%;
|
height: 4px;
|
background: linear-gradient(90deg, var(--themeColor), #ff8c00, #ff0080);
|
}
|
|
.el-tabs__header {
|
background: rgba(32, 32, 64, 0.5);
|
margin: 0;
|
border-bottom: 1px solid rgba(24, 144, 255, 0.2);
|
}
|
|
.el-tabs__item {
|
color: #a0a0ff;
|
font-weight: 500;
|
padding: 0 25px;
|
height: 50px;
|
line-height: 50px;
|
}
|
|
.el-tabs__item.is-active {
|
color: var(--themeColor);
|
background: rgba(24, 144, 255, 0.1);
|
}
|
|
.el-tabs__active-bar {
|
background-color: var(--themeColor);
|
height: 3px;
|
box-shadow: 0 0 10px var(--themeColor);
|
}
|
|
.el-tabs__content {
|
padding: 30px;
|
}
|
|
/* 远程控制卡片样式 */
|
.remote-control {
|
padding: 10px 0;
|
}
|
|
.control-card {
|
height: 220px;
|
border-radius: 16px;
|
cursor: pointer;
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
position: relative;
|
overflow: hidden;
|
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.2);
|
border: 1px solid transparent;
|
background: rgba(32, 32, 64, 0.5);
|
}
|
|
.control-card::before {
|
content: '';
|
position: absolute;
|
top: 0;
|
left: 0;
|
width: 100%;
|
height: 4px;
|
background: linear-gradient(90deg, var(--themeColor), #ff8c00, #ff0080);
|
}
|
|
.control-card:hover {
|
transform: translateY(-8px) scale(1.02);
|
box-shadow: 0 15px 35px rgba(0, 0, 0, 0.4);
|
border-color: rgba(24, 144, 255, 0.5);
|
}
|
|
.control-card:active {
|
transform: translateY(-2px) scale(0.98);
|
}
|
|
.control-card.loading {
|
cursor: not-allowed;
|
opacity: 0.7;
|
}
|
|
/* 卡片背景渐变 */
|
.restart-card {
|
background: linear-gradient(135deg, rgba(255, 214, 102, 0.1) 0%, rgba(255, 169, 64, 0.1) 100%);
|
}
|
|
.open-card {
|
background: linear-gradient(135deg, rgba(149, 222, 100, 0.1) 0%, rgba(82, 196, 26, 0.1) 100%);
|
}
|
|
.reset-card {
|
background: linear-gradient(135deg, rgba(255, 156, 110, 0.1) 0%, rgba(255, 77, 79, 0.1) 100%);
|
}
|
|
.card-content {
|
height: 100%;
|
display: flex;
|
flex-direction: column;
|
justify-content: center;
|
align-items: center;
|
position: relative;
|
z-index: 2;
|
padding: 20px;
|
}
|
|
.card-icon {
|
width: 70px;
|
height: 70px;
|
border-radius: 50%;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
margin-bottom: 20px;
|
background: rgba(255, 255, 255, 0.1);
|
backdrop-filter: blur(10px);
|
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
}
|
|
.restart-card .card-icon {
|
background: linear-gradient(135deg, rgba(255, 214, 102, 0.3) 0%, rgba(255, 169, 64, 0.3) 100%);
|
border-color: rgba(255, 214, 102, 0.5);
|
}
|
|
.open-card .card-icon {
|
background: linear-gradient(135deg, rgba(149, 222, 100, 0.3) 0%, rgba(82, 196, 26, 0.3) 100%);
|
border-color: rgba(149, 222, 100, 0.5);
|
}
|
|
.reset-card .card-icon {
|
background: linear-gradient(135deg, rgba(255, 156, 110, 0.3) 0%, rgba(255, 77, 79, 0.3) 100%);
|
border-color: rgba(255, 156, 110, 0.5);
|
}
|
|
.card-icon i {
|
font-size: 32px;
|
color: white;
|
text-shadow: 0 0 10px rgba(255, 255, 255, 0.5);
|
}
|
|
.control-card h3 {
|
color: white;
|
font-size: 20px;
|
font-weight: 600;
|
margin: 0 0 10px;
|
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
|
}
|
|
.card-description {
|
color: rgba(255, 255, 255, 0.8);
|
font-size: 14px;
|
text-align: center;
|
line-height: 1.2;
|
}
|
|
.card-hover-effect {
|
position: absolute;
|
bottom: 5px;
|
opacity: 0;
|
transform: translateY(20px);
|
transition: all 0.3s ease;
|
}
|
|
.control-card:hover .card-hover-effect {
|
opacity: 1;
|
transform: translateY(0);
|
}
|
|
.card-hover-effect span {
|
color: rgba(255, 255, 255, 0.9);
|
font-size: 12px;
|
font-weight: 500;
|
background: rgba(0, 0, 0, 0.3);
|
padding: 6px 15px;
|
border-radius: 20px;
|
backdrop-filter: blur(10px);
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
}
|
|
.loading-overlay {
|
position: absolute;
|
top: 0;
|
left: 0;
|
right: 0;
|
bottom: 0;
|
background: rgba(0, 0, 0, 0.7);
|
backdrop-filter: blur(4px);
|
display: flex;
|
flex-direction: column;
|
align-items: center;
|
justify-content: center;
|
z-index: 3;
|
border-radius: 16px;
|
}
|
|
.loading-overlay i {
|
font-size: 40px;
|
color: var(--themeColor);
|
margin-bottom: 10px;
|
animation: spin 1s linear infinite;
|
text-shadow: 0 0 10px var(--themeColor);
|
}
|
|
.loading-text {
|
color: #a0a0ff;
|
font-size: 14px;
|
}
|
|
@keyframes spin {
|
0% {
|
transform: rotate(0deg);
|
}
|
|
100% {
|
transform: rotate(360deg);
|
}
|
}
|
|
/* 设备升级样式 */
|
.firmware-upgrade {
|
padding: 10px 0;
|
}
|
|
.upgrade-card {
|
background: rgba(32, 32, 64, 0.5);
|
border-radius: 12px;
|
border: 1px solid rgba(24, 144, 255, 0.2);
|
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.2);
|
}
|
|
/* .upgrade-card::before {
|
content: '';
|
position: absolute;
|
top: 0;
|
left: 0;
|
width: 100%;
|
height: 4px;
|
background: linear-gradient(90deg, var(--themeColor), #ff8c00, #ff0080);
|
border-radius: 12px 12px 0 0;
|
} */
|
|
.upgrade-card h3 {
|
margin: 0 0 25px;
|
color: var(--themeColor);
|
font-weight: 500;
|
font-size: 16px;
|
display: flex;
|
align-items: center;
|
}
|
|
.upgrade-card h3 i {
|
margin-right: 10px;
|
font-size: 24px;
|
}
|
|
.upgrade-form {
|
margin-top: 10px;
|
}
|
|
.el-form-item__label {
|
color: #a0a0ff;
|
font-weight: 500;
|
}
|
|
.el-input__inner {
|
background: rgba(16, 16, 48, 0.7);
|
border: 1px solid rgba(24, 144, 255, 0.3);
|
color: #e0e0ff;
|
border-radius: 8px;
|
}
|
|
.el-input__inner:focus {
|
border-color: var(--themeColor);
|
box-shadow: 0 0 0 1px rgba(24, 144, 255, 0.2);
|
}
|
|
.el-input__prefix i {
|
color: var(--themeColor);
|
}
|
|
.el-button {
|
border-radius: 8px;
|
font-weight: 500;
|
transition: all 0.3s ease;
|
}
|
|
.el-button--primary {
|
background: var(--themeColor);
|
border: none;
|
box-shadow: 0 4px 15px rgba(24, 144, 255, 0.3);
|
}
|
|
.el-button--primary:hover {
|
transform: translateY(-2px);
|
box-shadow: 0 6px 20px rgba(24, 144, 255, 0.4);
|
}
|
|
/* 重置对话框样式 */
|
.reset-dialog .el-dialog {
|
background: rgba(16, 16, 48, 0.9);
|
border-radius: 15px;
|
border: 1px solid rgba(24, 144, 255, 0.3);
|
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.4);
|
backdrop-filter: blur(10px);
|
}
|
|
.reset-dialog .el-dialog__header {
|
background: rgba(32, 32, 64, 0.7);
|
border-bottom: 1px solid rgba(24, 144, 255, 0.2);
|
padding: 15px 20px;
|
border-radius: 15px 15px 0 0;
|
}
|
|
.reset-dialog .el-dialog__title {
|
color: #ff8c00;
|
font-weight: 600;
|
}
|
|
.reset-dialog .el-dialog__body {
|
padding: 20px;
|
}
|
|
.reset-warning {
|
margin-bottom: 20px;
|
}
|
|
.el-alert {
|
background: rgba(255, 154, 110, 0.1);
|
border: 1px solid rgba(255, 154, 110, 0.3);
|
color: #ff8c00;
|
}
|
|
.el-alert__title {
|
color: #ff8c00;
|
font-weight: 600;
|
}
|
|
.reset-dialog .el-dialog__footer {
|
background: rgba(32, 32, 64, 0.7);
|
border-top: 1px solid rgba(24, 144, 255, 0.2);
|
padding: 15px 20px;
|
border-radius: 0 0 15px 15px;
|
}
|
|
.el-button--danger {
|
background: linear-gradient(135deg, #ff6b6b, #ff4757);
|
border: none;
|
box-shadow: 0 4px 15px rgba(255, 107, 107, 0.3);
|
}
|
|
.el-button--danger:hover {
|
transform: translateY(-2px);
|
box-shadow: 0 6px 20px rgba(255, 107, 107, 0.4);
|
}
|
|
/* 文件上传样式 */
|
.upload-card {
|
background: rgba(32, 32, 64, 0.5);
|
border-radius: 12px;
|
border: 1px solid rgba(64, 224, 208, 0.2);
|
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.2);
|
padding: 25px;
|
}
|
|
.upload-card::before {
|
content: '';
|
position: absolute;
|
top: 0;
|
left: 0;
|
width: 100%;
|
height: 4px;
|
background: linear-gradient(90deg, #ff8c00, #ff0080, var(--themeColor));
|
border-radius: 12px 12px 0 0;
|
}
|
|
.upload-area {
|
border: 2px dashed rgba(24, 144, 255, 0.4);
|
border-radius: 12px;
|
padding: 40px 20px;
|
text-align: center;
|
transition: all 0.3s ease;
|
background: rgba(16, 16, 48, 0.3);
|
cursor: pointer;
|
}
|
|
.upload-area:hover {
|
border-color: var(--themeColor);
|
background: rgba(16, 16, 48, 0.5);
|
}
|
|
.upload-icon {
|
font-size: 48px;
|
color: var(--themeColor);
|
margin-bottom: 15px;
|
text-shadow: 0 0 10px rgba(24, 144, 255, 0.5);
|
}
|
|
.upload-text {
|
color: #a0a0ff;
|
margin-bottom: 10px;
|
}
|
|
.upload-hint {
|
color: #8080cc;
|
font-size: 12px;
|
margin-top: 10px;
|
}
|
|
.file-info {
|
margin-top: 20px;
|
padding: 15px;
|
background: rgba(16, 16, 48, 0.7);
|
border-radius: 8px;
|
border: 1px solid rgba(64, 224, 208, 0.2);
|
}
|
|
.file-info-item {
|
display: flex;
|
margin-bottom: 8px;
|
font-size: 14px;
|
}
|
|
.file-info-item:last-child {
|
margin-bottom: 0;
|
}
|
|
.file-label {
|
color: #a0a0ff;
|
width: 80px;
|
flex-shrink: 0;
|
}
|
|
.file-value {
|
color: #e0e0ff;
|
word-break: break-all;
|
}
|
|
.upload-actions {
|
margin-top: 20px;
|
display: flex;
|
gap: 10px;
|
}
|
|
.upload-actions .el-button {
|
flex: 1;
|
}
|
|
/* 响应式设计 */
|
@media (max-width: 768px) {
|
#device-control-panel {
|
padding: 15px;
|
}
|
|
.el-tabs__content {
|
padding: 20px 15px;
|
}
|
|
.control-card {
|
height: 180px;
|
margin-bottom: 20px;
|
}
|
|
.card-icon {
|
width: 60px;
|
height: 60px;
|
}
|
|
.card-icon i {
|
font-size: 28px;
|
}
|
|
.control-card h3 {
|
font-size: 18px;
|
}
|
}
|
|
@media (max-width: 480px) {
|
.control-card {
|
height: 160px;
|
}
|
|
.card-icon {
|
width: 50px;
|
height: 50px;
|
margin-bottom: 15px;
|
}
|
|
.card-icon i {
|
font-size: 24px;
|
}
|
|
.control-card h3 {
|
font-size: 16px;
|
}
|
|
.card-description {
|
font-size: 12px;
|
}
|
}
|
</style>
|