You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
central-control/src/views/Monitoring/index.vue

1247 lines
34 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<div class="control-view">
<!-- 主要内容 -->
<div class="main-content">
<!-- 顶部状态栏 -->
<div class="header-status">
<div class="time-info">
<span>{{ currentTime }} {{ week }}</span>
</div>
<div class="page-title">
<h1>实时监控</h1>
</div>
<div class="system-status">
<!-- <span>系统已运行{{ systemRuntime }}</span> -->
</div>
</div>
<!-- 主体区域 -->
<div class="control-content">
<!-- 左侧电力参数区域 -->
<div class="power-section">
<!-- ABC三相电力参数 -->
<div class="power-params">
<div v-for="phase in powerPhases" :key="phase.name" class="phase-card">
<div class="phase-name"><span>{{ phase.name }}</span>相</div>
<div class="param-list">
<div class="param-item">
<span class="param-label">电压:</span>
<span class="param-value">{{ phase.voltage }}</span>
</div>
<div class="param-item">
<span class="param-label">电流:</span>
<span class="param-value">{{ phase.current }}</span>
</div>
<div class="param-item">
<span class="param-label">有功功率:</span>
<span class="param-value">{{ phase.power }}</span>
</div>
<div class="param-item">
<span class="param-label">功率因数:</span>
<span class="param-value">{{ phase.factor }}</span>
</div>
</div>
</div>
<!-- 电能参数 -->
<div class="energy-card">
<div class="energy-name"><span>电</span><span>能</span><span>参</span><span>数</span></div>
<div class="param-list">
<!-- <div class="param-item">
<span class="param-label">DC电压:</span>
<span class="param-value">{{ energyParams.dcVoltage }}</span>
</div> -->
<div class="param-item">
<span class="param-label">有功电能:</span>
<span class="param-value">{{ energyParams.activeEnergy }}</span>
</div>
<div class="param-item">
<span class="param-label">有功功率:</span>
<span class="param-value">{{ energyParams.activePower }}</span>
</div>
<div class="param-item">
<span class="param-label">功率因数:</span>
<span class="param-value">{{ energyParams.powerFactor }}</span>
</div>
</div>
</div>
<div class="control-group">
<div class="control-title">
<el-icon>
<Setting />
</el-icon>
当前模式
</div>
<el-button style="margin-bottom: 0;" class="mode-button active">{{ info.Mode ? '手动' : '自动' }}</el-button>
</div>
</div>
<div class="fault-section" :class="{ 'fault-section-expanded': faultListExpanded }">
<div class="fault-header" @click="toggleFaultList">
<div>
<div class="fault-title">
当前故障
<span v-if="currentFaults.length" class="fault-count">{{ currentFaults.length }}个</span>
<span class="fault-subtitle">当前扫描设备数:{{ scanDeviceCount }} · {{ faultStatusText }}</span>
</div>
</div>
<div class="fault-actions">
<el-button v-if="currentFaults.length" class="fault-toggle-button" @click.stop="toggleFaultList">
{{ faultListExpanded ? '隐藏' : '展开' }}
</el-button>
<el-button class="fault-refresh-button" :loading="faultRefreshing" @click.stop="refreshCurrentFaults">
<el-icon>
<RefreshRight />
</el-icon>
刷新
</el-button>
</div>
</div>
<div v-if="currentFaults.length && faultListExpanded" class="fault-list" @click.stop>
<div v-for="(fault, index) in visibleCurrentFaults" :key="`${fault.loop_idx}-${fault.code}-${fault.message}-${index}`" class="fault-item">
<span class="fault-loop">回路{{ fault.loop_idx }}</span>
<span class="fault-message" :title="fault.code ? `${fault.message}(${fault.code})` : fault.message">{{ fault.message }}</span>
</div>
<div v-if="hiddenFaultCount" class="fault-item fault-more">+{{ hiddenFaultCount }}</div>
</div>
</div>
<div class="circuit-section">
<div class="circuit-grid">
<div v-for="(circuit, index) in monitorLoops" :key="`${circuit.name}-${index}`" class="circuit-item">
<div class="circuit-status" :class="{ active: circuit.is_open }">
直流回路{{ loopNo(circuit, index) }}{{ circuit.is_open ? '打开' : '关闭' }}
</div>
<div class="circuitStatus">
<div class="line">
<div class="line_item">电压: <span>{{ circuit.V ? circuit.V?.toFixed(2) : 0 }}V</span></div>
<div class="line_item">电流: <span>{{ circuit.A ? circuit.A?.toFixed(2) : 0 }}A</span></div>
</div>
<div class="line">
<div class="line_item line_item_full">功率: <span>{{ circuit.Kw ? circuit.Kw?.toFixed(3) : 0 }}kW</span></div>
</div>
</div>
</div>
</div>
</div>
<div class="dimming-section">
<div class="dimming-grid">
<div v-for="(channel, index) in monitorLoops" :key="`${channel.name}-${index}`" class="dimming-item">
<div class="channel-label">调光通道{{ loopNo(channel, index) }}</div>
<div class="brightness-display" :class="{ active: channel.pwm > 0 }">
{{ channel.pwm || 0 }}%
</div>
</div>
</div>
</div>
<!-- <div class="chartBox">
<div class="search">
<el-date-picker value-format="YYYY-MM-DD" v-model="recdDate" type="date" placeholder="请选择时间" />
<el-button type="primary" class="mode-button active" style="width:70px;height: 32px; margin-bottom: 0;" :loading="lineDataLoading" @click="getLineData">查询</el-button>
</div>
<Linechart :lineData="lineData"></Linechart>
</div> -->
</div>
</div>
</div>
<!-- 底部信息 -->
<div class="bottom-info">
<!-- <div class="bottom_item">
<div class="time-item">
<span>日出时间{{ sunriseTime }}</span>
</div>
<div class="time-item">
<span>日落时间{{ sunsetTime }}</span>
</div>
</div> -->
<div style="color:#fff">
设备号{{ commit_uid }}
</div>
<div class="bottom_item">
<!-- <el-button class="restart-button" @click="restartSystem">
<el-icon>
<RefreshRight />
</el-icon>
远程重启
</el-button> -->
<el-button class="back-button" @click="goBack">
<el-icon>
<HomeFilled />
</el-icon>
返回主界面
</el-button>
</div>
</div>
<!-- 亮度调节弹窗 -->
<el-dialog v-model="brightnessDialogVisible" title="亮度调节" width="400px" :modal="true" class="brightness-dialog">
<div class="brightness-content">
<div class="brightness-label">亮度值:{{ currentBrightness }}%</div>
<div class="brightness-slider-container">
<el-slider v-model="currentBrightness" :min="0" :max="100" :show-stops="true" class="brightness-slider" />
</div>
</div>
<template #footer>
<div class="dialog-footer">
<el-button @click="brightnessDialogVisible = false">取消</el-button>
<el-button type="primary" @click="confirmBrightness">确认</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { computed, ref, onMounted, onUnmounted, watch } from 'vue'
import { useRouter } from 'vue-router'
import RPC from '@/utils/rpc'
import dayjs from 'dayjs'
import Linechart from '@/components/Linechart.vue';
// const commit_uid = '868656077692382'
const commit_uid = sessionStorage.getItem('central_control_comm_uid')
const router = useRouter()
const currentTime = ref('')
const systemRuntime = ref('38分钟')
const brightnessDialogVisible = ref(false)
const currentBrightness = ref(50)
const selectedChannelName = ref(null)
const faultRefreshing = ref(false)
const faultListExpanded = ref(false)
const week = ref('')
const weekDays = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
function transformHourlyData(data) {
if (!data) return []
return Object.keys(data)
.filter(key => key.startsWith('hour'))
.map(key => {
const hourNum = parseInt(key.replace('hour', ''), 10);
return {
name: hourNum.toString().padStart(2, '0'),
value: data[key]
};
})
.sort((a, b) => a.name.localeCompare(b.name));
}
const lineData = ref([])
const recdDate = ref(dayjs().add(-1, 'day').format('YYYY-MM-DD'))
const lineDataLoading = ref(false)
// const getLineData = () => {
// lineDataLoading.value = true
// RPC.post('/lightm/rtu/his', { commUid: commit_uid, recdDate: recdDate.value }).then(res => {
// lineData.value = transformHourlyData(res)
// }).finally(() => {
// lineDataLoading.value = false
// })
// }
// getLineData()
// 电力参数数据
const powerPhases = ref([
{
name: 'A',
voltage: '0V',
current: '0A',
power: '0kW',
factor: '0'
},
{
name: 'B',
voltage: '0V',
current: '0A',
power: '0kW',
factor: '0'
},
{
name: 'C',
voltage: '0V',
current: '0A',
power: '0kW',
factor: '0'
}
])
const energyParams = ref({
dcVoltage: '0.0V',
activeEnergy: '0kwh',
activePower: '0kW',
powerFactor: '0'
})
const info = ref({
"Mode": 0,
"scan_device_count": 10,
"backup_byte1": 0,
"Loops": [
{
"index": 0,
"name": "#1",
"is_open": false,
"pwm": 0,
A: 0,
V: 0,
Kw: 0,
},
{
"index": 1,
"name": "#2",
"is_open": false,
"pwm": 0,
A: 0,
V: 0,
Kw: 0,
},
{
"index": 2,
"name": "#3",
"is_open": false,
"pwm": 0,
A: 0,
V: 0,
Kw: 0,
},
{
"index": 3,
"name": "#4",
"is_open": false,
"pwm": 0,
A: 0,
V: 0,
Kw: 0,
},
{
"index": 4,
"name": "#5",
"is_open": false,
"pwm": 0,
A: 0,
V: 0,
Kw: 0,
},
{
"index": 5,
"name": "#6",
"is_open": false,
"pwm": 0,
A: 0,
V: 0,
Kw: 0,
},
{
"index": 6,
"name": "#7",
"is_open": false,
"pwm": 0,
A: 0,
V: 0,
Kw: 0,
},
{
"index": 7,
"name": "#8",
"is_open": false,
"pwm": 0,
A: 0,
V: 0,
Kw: 0,
},
{
"index": 8,
"name": "#9",
"is_open": false,
"pwm": 0,
A: 0,
V: 0,
Kw: 0,
},
{
"index": 9,
"name": "#10",
"is_open": false,
"pwm": 0,
A: 0,
V: 0,
Kw: 0,
}
],
"backup_byte4": 0
})
const scanDeviceCount = computed(() => {
return info.value?.scan_device_count ?? info.value?.ScanDeviceCount ?? info.value?.Loops?.length ?? 0
})
const monitorLoops = computed(() => {
return info.value?.Loops || []
})
const loopNo = (loop, index) => {
const loopIndex = loop?.index ?? loop?.Index
return Number.isInteger(loopIndex) ? loopIndex + 1 : index + 1
}
const maxVisibleFaultCount = 10
const currentFaults = computed(() => {
const loops = info.value?.Loops || []
return loops.flatMap((loop, index) => {
const loopIdx = loop.loop_idx || loop.LoopIdx || loop.index + 1 || index + 1
const faults = loop.faults || loop.Faults || []
if (Array.isArray(faults) && faults.length > 0) {
return faults.map((fault) => ({
loop_idx: fault.loop_idx || loopIdx,
message: fault.message || fault.fault_msg || fault.Message || '',
code: fault.code || fault.fault_code || fault.Code || '',
})).filter((fault) => fault.message)
}
const message = loop.fault_msg || loop.FaultMsg
if ((loop.has_fault || loop.HasFault) && message) {
return [{
loop_idx: loopIdx,
message,
code: loop.fault_code || loop.FaultCode || '',
}]
}
return []
})
})
const visibleCurrentFaults = computed(() => {
if (currentFaults.value.length > maxVisibleFaultCount) {
return currentFaults.value.slice(0, maxVisibleFaultCount - 1)
}
return currentFaults.value
})
const hiddenFaultCount = computed(() => currentFaults.value.length - visibleCurrentFaults.value.length)
const faultStatusText = computed(() => {
if (!currentFaults.value.length) return '当前无故障'
return faultListExpanded.value ? '已展开' : '已隐藏'
})
const toggleFaultList = () => {
if (!currentFaults.value.length) return
faultListExpanded.value = !faultListExpanded.value
}
watch(currentFaults, (faults) => {
if (!faults.length) {
faultListExpanded.value = false
}
})
const loadDeviceStatus = () => {
return RPC.post('/m9z/getDeviceStatus2', { comm_uid: commit_uid }).then((res) => {
info.value = res
})
}
const loadPowerData = () => {
return RPC.post('/adl400/readPhaseData', { comm_uid: commit_uid }).then(res => {
powerPhases.value[0].voltage = res.aVoltage + 'V'
powerPhases.value[0].current = res.aElectricCurrent + 'A'
powerPhases.value[0].power = res.aPower + 'kW'
powerPhases.value[0].factor = res.aPowerFactor
powerPhases.value[1].voltage = res.bVoltage + 'V'
powerPhases.value[1].current = res.bElectricCurrent + 'A'
powerPhases.value[1].power = res.bPower + 'kW'
powerPhases.value[1].factor = res.bPowerFactor
powerPhases.value[2].voltage = res.cVoltage + 'V'
powerPhases.value[2].current = res.cElectricCurrent + 'A'
powerPhases.value[2].power = res.cPower + 'kW'
powerPhases.value[2].factor = res.cPowerFactor
energyParams.value.activeEnergy = res.activeEnergy + 'kW·h'
energyParams.value.activePower = res.activePower + 'kW'
energyParams.value.powerFactor = res.powerFactor
})
}
const getInfo = () => {
// RPC.post('/m9z/getDeviceManualInfo', { comm_uid: commit_uid }).then(res => {
// info.value = res
// })
return loadDeviceStatus().finally(() => loadPowerData())
// RPC.post('/m9z/getDeviceSunRiseTime', { comm_uid: commit_uid }).then(res => {
// sunriseTime.value = res.Sunrise ? dayjs(res.Sunrise).format('HH:mm:ss') : ''
// sunsetTime.value = res.Sunset ? dayjs(res.Sunset).format('HH:mm:ss') : ''
// })
}
getInfo()
const refreshCurrentFaults = () => {
faultRefreshing.value = true
loadDeviceStatus()
.then(() => {
ElMessage.success('当前故障已刷新')
})
.finally(() => {
faultRefreshing.value = false
})
}
let timer = null
RPC.post('/getMessageInterval').then(res => {
console.log(`获取消息间隔成功,消息间隔为${res * 1000}毫秒`)
timer = setInterval(() => {
getInfo()
}, res * 1000)
}).catch(() => {
timer = setInterval(() => {
console.log(`获取消息间隔失败消息间隔为30秒`)
getInfo()
}, 30000)
})
onUnmounted(() => {
if (timer) {
clearInterval(timer)
}
})
const oldInfo = ref('')
// watch(info, () => {
// if (oldInfo.value === JSON.stringify(info.value)) return
// RPC.post('/m9z/setDeviceManualInfo', { comm_uid: commit_uid, ...info.value }).finally(() => {
// if (!isSync.value) {
// isSync.value = true
// getInfo()
// }
// })
// oldInfo.value = JSON.stringify(info.value)
// }, { deep: true })
const sunriseTime = ref('')
const sunsetTime = ref('')
// 更新时间
const updateTime = () => {
const now = new Date()
const year = now.getFullYear()
const month = String(now.getMonth() + 1).padStart(2, '0')
const day = String(now.getDate()).padStart(2, '0')
const hours = String(now.getHours()).padStart(2, '0')
const minutes = String(now.getMinutes()).padStart(2, '0')
const seconds = String(now.getSeconds()).padStart(2, '0')
week.value = weekDays[now.getDay()]
currentTime.value = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
}
// 切换回路状态
const toggleCircuit = (circuitName) => {
if (!info.value.disable) return ElMessage.warning('请切换至手动模式')
const circuit = info.value.loops.find(c => c.name === circuitName)
if (circuit) {
circuit.is_open = !circuit.is_open
}
}
// 打开亮度调节弹窗
const openBrightnessDialog = (channelName) => {
if (!info.value.disable) return ElMessage.warning('请切换至手动模式')
selectedChannelName.value = channelName
const channel = info.value.loops.find(c => c.name === channelName)
if (channel) {
currentBrightness.value = channel.pwm
brightnessDialogVisible.value = true
}
}
// 确认亮度设置
const confirmBrightness = () => {
const channel = info.value.loops.find(c => c.name === selectedChannelName.value)
if (channel) {
channel.pwm = currentBrightness.value
// ElMessage.success(`${channel.name}亮度已设置为${currentBrightness.value}%`)
} else {
info.value.loops.forEach(channel => {
channel.pwm = currentBrightness.value
})
}
brightnessDialogVisible.value = false
}
// 打开全局亮度调节
const openGlobalBrightnessDialog = () => {
if (!info.value.disable) return ElMessage.warning('请切换至手动模式')
selectedChannelName.value = null
currentBrightness.value = 50
brightnessDialogVisible.value = true
}
// 全开灯光
const openLightSettings = () => {
if (!info.value.disable) return ElMessage.warning('请切换至手动模式')
info.value.loops.forEach(circuit => {
circuit.is_open = true
})
// dimmingChannels.value.forEach(channel => {
// channel.brightness = 100
// })
}
// 全关灯光
const turnOffAll = () => {
if (!info.value.disable) return ElMessage.warning('请切换至手动模式')
info.value.loops.forEach(circuit => {
circuit.is_open = false
})
// dimmingChannels.value.forEach(channel => {
// channel.brightness = 0
// })
// ElMessage.success('所有灯光已关闭')
}
// 返回主界面
const goBack = () => {
router.push('/')
}
// 重启系统
const restartSystem = () => {
RPC.post('/m9z/setRestart', { comm_uid: commit_uid }).then(res => {
ElMessage.success('远程重启成功')
getInfo()
})
}
let timeInterval = null
// 处理 session 恢复后的重新加载
const handleSessionRestored = () => {
console.log('Session 已恢复,重新加载数据')
getInfo()
}
onMounted(() => {
updateTime()
timeInterval = setInterval(updateTime, 1000)
// 监听 session 恢复事件
window.addEventListener('m9z:session-restored', handleSessionRestored)
})
onUnmounted(() => {
if (timeInterval) {
clearInterval(timeInterval)
}
// 移除事件监听
window.removeEventListener('m9z:session-restored', handleSessionRestored)
})
</script>
<style scoped lang="scss">
.control-view {
width: 100vw;
height: 100vh;
box-sizing: border-box;
position: relative;
overflow: hidden;
background: center / cover url('@/assets/images/@2x.png') no-repeat;
background-color: #0A3933;
}
.main-content {
position: relative;
z-index: 2;
width: 100%;
height: 100%;
overflow: hidden;
padding: 8px 20px 52px;
box-sizing: border-box;
display: flex;
flex-direction: column;
}
.header-status {
display: flex;
justify-content: space-between;
align-items: center;
flex-shrink: 0;
margin-bottom: 8px;
color: rgba(255, 255, 255, 0.9);
}
.time-info {
font-size: 18px;
font-weight: 300;
width: 270px;
}
.page-title h1 {
font-size: 26px;
font-weight: bold;
margin: 0;
color: #ffffff;
text-shadow: 0 0 10px rgba(255, 255, 255, 0.3);
}
.system-status {
min-width: 200px;
font-size: 18px;
font-weight: 300;
width: 270px;
}
.control-content {
display: flex;
width: 100%;
gap: 20px;
box-sizing: border-box;
flex: 1;
min-height: 0;
}
.power-section {
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
/* width: 500px; */
gap: 6px;
height: 100%;
min-height: 0;
overflow: hidden;
}
.power-params {
display: flex;
justify-content: space-between;
gap: 8px;
flex-shrink: 0;
}
.phase-card,
.energy-card {
display: flex;
background: rgba(255, 255, 255, 0.08);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 12px;
width: 23%;
overflow: hidden;
/* padding: 15px; */
backdrop-filter: blur(10px);
}
.phase-name,
.energy-name {
font-size: 14px;
color: rgba(255, 255, 255, 0.7);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin-bottom: 15px;
background-color: #24806B;
width: 30px;
height: 100%;
}
.param-list {
display: flex;
flex-direction: column;
padding: 4px 8px;
flex: 1;
gap: 3px;
justify-content: space-between;
}
.param-item {
display: flex;
justify-content: space-between;
font-size: 11px;
}
.param-label {
color: rgba(255, 255, 255, 0.7);
}
.param-value {
color: rgba(255, 255, 255, 0.9);
font-weight: 500;
}
.fault-section {
position: relative;
z-index: 5;
background: rgba(255, 255, 255, 0.08);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 8px;
padding: 5px 10px;
color: #fff;
flex-shrink: 0;
}
.fault-section-expanded {
z-index: 20;
}
.fault-header {
display: flex;
justify-content: space-between;
align-items: center;
gap: 10px;
cursor: pointer;
}
.fault-title {
display: flex;
align-items: center;
gap: 6px;
font-size: 14px;
font-weight: 600;
}
.fault-count {
padding: 1px 5px;
border-radius: 10px;
font-size: 11px;
color: #ffd4d4;
background: rgba(220, 53, 69, 0.18);
}
.fault-subtitle {
font-size: 12px;
color: rgba(255, 255, 255, 0.68);
}
.fault-actions {
display: flex;
align-items: center;
gap: 6px;
flex-shrink: 0;
}
.fault-toggle-button {
flex-shrink: 0;
height: 26px;
padding: 0 8px;
border-radius: 6px;
border: 1px solid rgba(255, 255, 255, 0.2);
color: #fff;
background: rgba(255, 255, 255, 0.1);
}
.fault-refresh-button {
flex-shrink: 0;
height: 26px;
padding: 0 8px;
border-radius: 6px;
border: none;
color: #fff;
background: linear-gradient(180deg, rgba(51, 224, 188, 1) 0%, rgba(5, 149, 117, 1) 100%);
}
.fault-list {
position: absolute;
top: auto;
bottom: calc(100% + 5px);
left: 0;
right: 0;
z-index: 30;
display: grid;
grid-template-columns: repeat(5, minmax(0, 1fr));
grid-template-rows: repeat(2, 21px);
gap: 4px;
padding: 5px 10px;
overflow: hidden;
box-sizing: border-box;
border-radius: 8px;
background: rgba(42, 69, 66, 0.96);
border: 1px solid rgba(255, 120, 120, 0.35);
box-shadow: 0 10px 24px rgba(0, 0, 0, 0.28);
}
.fault-item {
display: flex;
align-items: center;
gap: 4px;
min-height: 21px;
min-width: 0;
padding: 2px 5px;
border-radius: 6px;
background: rgba(220, 53, 69, 0.16);
border: 1px solid rgba(255, 120, 120, 0.35);
}
.fault-loop {
flex-shrink: 0;
font-size: 10px;
color: #ffd4d4;
}
.fault-message {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 11px;
font-weight: 600;
color: #fff;
}
.fault-more {
justify-content: center;
font-size: 11px;
font-weight: 600;
color: #ffd4d4;
}
.circuit-section {
display: flex;
flex-direction: column;
gap: 6px;
flex: 1 1 auto;
min-height: 0;
}
.circuit-grid {
display: grid;
grid-template-columns: repeat(5, minmax(0, 1fr));
grid-template-rows: repeat(2, minmax(0, 1fr));
gap: 6px;
height: 100%;
min-height: 0;
font-size: 10px;
.circuitStatus {
color: #fff;
width: 100%;
.line {
width: 100%;
display: flex;
margin-top: 3px;
justify-content: space-between;
.line_item {
width: 48%;
// flex: 1;
flex-shrink: 0;
display: flex;
justify-content: space-between;
&.line_item_full {
width: 100%;
}
}
}
}
}
.circuit-item {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-width: 0;
min-height: 0;
padding: 4px 5px;
border-radius: 8px;
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.12);
}
.circuit-status {
background: rgba(255, 255, 255, 0.08);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 8px;
padding: 3px 5px;
color: rgba(255, 255, 255, 0.7);
text-align: center;
width: 100%;
box-sizing: border-box;
}
.circuit-status.active {
background: rgba(33, 150, 243, 0.2);
border-color: rgba(33, 150, 243, 0.5);
color: #64b5f6;
}
.circuit-icon {
width: 30px;
height: 30px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.1);
border: 2px solid rgba(255, 255, 255, 0.2);
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.3s ease;
color: rgba(255, 255, 255, 0.5);
}
.circuit-icon.active {
background: rgba(255, 193, 7, 0.2);
border-color: rgba(255, 193, 7, 0.6);
color: #ffc107;
box-shadow: 0 0 15px rgba(255, 193, 7, 0.3);
}
.circuit-icon:hover {
transform: scale(1.05);
box-shadow: 0 0 20px rgba(255, 255, 255, 0.2);
}
.dimming-section {
display: flex;
flex-direction: column;
gap: 5px;
font-size: 11px;
flex-shrink: 0;
}
.dimming-grid {
display: grid;
grid-template-columns: repeat(5, minmax(0, 1fr));
gap: 5px 8px;
}
.dimming-item {
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
min-width: 0;
}
.channel-label {
color: rgba(255, 255, 255, 0.8);
text-align: center;
}
.brightness-display {
background: rgba(255, 255, 255, 0.08);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 8px;
padding: 4px 6px;
color: rgba(255, 255, 255, 0.7);
font-weight: bold;
text-align: center;
width: 100%;
box-sizing: border-box;
transition: all 0.3s ease;
}
.brightness-display.active {
background: rgba(33, 150, 243, 0.3);
border-color: rgba(33, 150, 243, 0.6);
color: #64b5f6;
box-shadow: 0 0 10px rgba(33, 150, 243, 0.3);
}
.bottom-info {
display: flex;
justify-content: space-between;
align-items: center;
border-top: 1px solid #eee;
width: 100vw;
padding: 0 24px;
height: 44px;
position: fixed;
bottom: 0;
left: 0;
z-index: 999;
box-sizing: border-box;
.bottom_item {
display: flex;
align-items: center;
}
.time-item {
color: rgba(255, 255, 255, 0.7);
font-size: 16px;
margin: 0 20px;
}
.back-button,
.restart-button {
width: 100%;
height: 34px;
border-radius: 17px;
font-size: 15px;
font-weight: 500;
transition: all 0.3s ease;
}
.back-button {
// background-color: #000;
// color: #fff;
border: #000;
background: linear-gradient(180deg, rgba(223, 196, 137, 1) 0%, rgba(190, 151, 99, 1) 100%);
color: #fff;
}
}
.control-panel {
width: 150px;
display: flex;
flex-direction: column;
justify-content: space-between;
gap: 20px;
}
.control-group {
width: 150px;
background: rgba(255, 255, 255, 0.08);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 12px;
padding: 0 10px;
backdrop-filter: blur(10px);
display: flex;
flex-direction: column;
justify-content: center;
}
.control-title {
color: rgba(255, 255, 255, 0.9);
font-size: 16px;
font-weight: 500;
text-align: center;
margin-bottom: 15px;
display: flex;
align-items: center;
.el-icon {
font-size: 20px;
margin-right: 10px;
}
}
.mode-button,
.setting-button,
.brightness-button {
width: 100%;
height: 45px;
border-radius: 8px;
font-size: 16px;
font-weight: 500;
transition: all 0.3s ease;
margin-bottom: 10px;
}
.mode-button {
background: rgba(255, 255, 255, 0.9);
border: none;
color: #333;
}
.mode-button.active {
background: linear-gradient(180deg, rgba(51, 224, 188, 1) 0%, rgba(5, 149, 117, 1) 100%);
color: #fff;
}
.setting-button {
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.3);
color: rgba(255, 255, 255, 0.9);
}
.setting-button.active {
background: #1976d2;
border-color: #1976d2;
color: #ffffff;
}
.brightness-button {
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.3);
color: rgba(255, 255, 255, 0.9);
}
.setting-button:hover,
.brightness-button:hover {
background: rgba(255, 255, 255, 0.15);
transform: translateY(-2px);
}
/* */
:deep(.brightness-dialog) {
background: rgba(30, 30, 30, 0.95);
backdrop-filter: blur(20px);
}
:deep(.brightness-dialog .el-dialog__header) {
// background: rgba(255, 255, 255, 0.1);
border-bottom: 1px solid rgba(255, 255, 255, 0.2);
}
:deep(.brightness-dialog .el-dialog__title) {
color: #ffffff;
font-size: 18px;
font-weight: 500;
}
:deep(.brightness-dialog .el-dialog__body) {
// background: rgba(255, 255, 255, 0.05);
color: #ffffff;
}
.brightness-content {
padding: 20px 0;
}
.brightness-label {
text-align: center;
font-size: 18px;
font-weight: 500;
margin-bottom: 30px;
color: #ffffff;
}
.brightness-slider-container {
padding: 0 20px;
}
:deep(.brightness-slider .el-slider__bar) {
background: #1976d2;
}
:deep(.brightness-slider .el-slider__button) {
border-color: #1976d2;
}
:deep(.brightness-slider .el-slider__stop) {
background: rgba(255, 255, 255, 0.3);
}
.dialog-footer {
display: flex;
justify-content: center;
gap: 20px;
padding-top: 20px;
}
.chartBox {
width: 100%;
.search {
display: flex;
align-items: center;
margin-bottom: 15px;
.el-button {
background-color: #006eff;
border-color: #006eff;
margin-left: 20px;
}
}
}
</style>