<template>
|
<div class="cond-card" :class="{ offline: !isOnline }">
|
<div class="cond-card__head">
|
<span class="cond-card__name" :title="displayName">{{ displayName }}</span>
|
<i class="cond-card__online" :class="isOnline ? 'el-icon-success online' : 'el-icon-error offline'" :title="device.online || '离线'"></i>
|
</div>
|
<div class="cond-card__uptime" v-if="device.uptime">运行 {{ device.uptime }}</div>
|
<div class="cond-card__temp">
|
<div class="temp-set">
|
<el-button type="text" class="temp-btn" icon="el-icon-arrow-down" @click="$emit('set-temp', device, -1)" v-permissions="['business:ywconditioner:operate']" />
|
<span class="temp-num">{{ formatTemp(device.tempSet) }}</span>
|
<el-button type="text" class="temp-btn" icon="el-icon-arrow-up" @click="$emit('set-temp', device, 1)" v-permissions="['business:ywconditioner:operate']" />
|
<span class="temp-label">【设定温度】</span>
|
</div>
|
<div class="temp-indoor">
|
<span class="temp-num sm">{{ formatTemp(device.temp) }}</span>
|
<span class="temp-label">【室内温度】</span>
|
</div>
|
</div>
|
<div class="cond-card__modes">
|
<span class="cond-card__row-label">模式:</span>
|
<span
|
v-for="item in modeOptions"
|
:key="'m' + item.value"
|
class="mode-icon"
|
:class="{ active: device.mode === item.value, 'accent-red': item.accentRed && device.mode === item.value }"
|
:title="item.label"
|
@click="$emit('set-mode', device, item.value)"
|
v-permissions="['business:ywconditioner:operate']"
|
>{{ item.icon }}</span>
|
</div>
|
<div class="cond-card__fans">
|
<span class="cond-card__row-label">风速:</span>
|
<span
|
v-for="item in fanOptions"
|
:key="'f' + item.value"
|
class="fan-icon"
|
:class="{ active: currentFan === item.value, 'accent-red': item.accentRed && currentFan === item.value }"
|
:title="item.label"
|
@click="$emit('set-fan', device, item.value)"
|
v-permissions="['business:ywconditioner:operate']"
|
>{{ item.short }}</span>
|
</div>
|
<div class="cond-card__pwr">
|
<span class="pwr-dot" :class="{ on: device.pwr === 1 }"></span>
|
{{ device.pwr === 1 ? '运行中' : '已关机' }}
|
<span v-if="device.ktLock === 1" class="lock-tag">锁定</span>
|
</div>
|
<div class="cond-card__actions">
|
<el-button size="mini" plain @click="$emit('history', device)">历史</el-button>
|
<el-button
|
v-if="isDeviceLocked"
|
size="mini"
|
type="danger"
|
plain
|
:loading="lockLoading"
|
@click="$emit('unlock', device)"
|
v-permissions="['business:ywconditioner:operate']"
|
>解锁</el-button>
|
<el-button
|
v-else
|
size="mini"
|
plain
|
:loading="lockLoading"
|
@click="$emit('lock', device)"
|
v-permissions="['business:ywconditioner:operate']"
|
>锁定</el-button>
|
<el-button
|
size="mini"
|
:type="device.pwr === 1 ? 'danger' : 'primary'"
|
:loading="powerLoading"
|
@click="$emit('toggle-power', device)"
|
v-permissions="['business:ywconditioner:operate']"
|
>{{ device.pwr === 1 ? '关机' : '开机' }}</el-button>
|
</div>
|
<div class="cond-card__meta" v-if="device.wgMac">
|
<span v-if="device.wgMac" class="wg-block">
|
<span class="wg-mac" :title="device.wgMac">{{ device.wgMac }}</span>
|
<span v-if="device.wgBz" class="wg-bz" :title="device.wgBz">{{ device.wgBz }}</span>
|
</span>
|
</div>
|
</div>
|
</template>
|
|
<script>
|
const MODE_OPTIONS = [
|
{ value: 1, label: '制热', icon: '☀', accentRed: true },
|
{ value: 2, label: '制冷', icon: '❄' },
|
{ value: 3, label: '送风', icon: '风' },
|
{ value: 4, label: '除湿', icon: '湿' }
|
]
|
|
const FAN_OPTIONS = [
|
{ value: 1, label: '低速', short: 'L' },
|
{ value: 2, label: '中速', short: 'M' },
|
{ value: 3, label: '高速', short: 'H' },
|
{ value: 4, label: '自动', short: 'A', accentRed: true }
|
]
|
|
export default {
|
name: 'YwConditionerCard',
|
props: {
|
device: {
|
type: Object,
|
required: true
|
},
|
powerLoading: {
|
type: Boolean,
|
default: false
|
},
|
lockLoading: {
|
type: Boolean,
|
default: false
|
}
|
},
|
data () {
|
return {
|
modeOptions: MODE_OPTIONS,
|
fanOptions: FAN_OPTIONS
|
}
|
},
|
computed: {
|
isOnline () {
|
const o = this.device.online
|
if (o === '在线' || o === '1' || o === 1) return true
|
if (o === 88 || o === 66 || o === '88' || o === '66') return true
|
return false
|
},
|
displayName () {
|
const parts = [this.device.floorName, this.device.roomName, this.device.name]
|
.filter(p => p != null && String(p).trim() !== '')
|
return parts.length ? parts.join('/') : '-'
|
},
|
currentFan () {
|
return this.device.fanSet != null ? this.device.fanSet : this.device.fan
|
},
|
isDeviceLocked () {
|
return this.device.ktLock === 1
|
}
|
},
|
methods: {
|
formatTemp (val) {
|
if (val === null || val === undefined || val === '') return '--'
|
const n = Number(val)
|
if (isNaN(n)) return val
|
const celsius = n > 100 ? n / 10 : n
|
return celsius.toFixed(1) + '℃'
|
}
|
}
|
}
|
</script>
|
|
<style scoped>
|
.cond-card {
|
background: linear-gradient(145deg, #2c3e50 0%, #1a252f 100%);
|
border-radius: 10px;
|
padding: 14px 16px;
|
color: #e8ecf0;
|
min-height: 220px;
|
display: flex;
|
flex-direction: column;
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
transition: opacity 0.2s;
|
}
|
.cond-card.offline { opacity: 0.75; }
|
.cond-card__head {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
margin-bottom: 6px;
|
}
|
.cond-card__name {
|
font-weight: 600;
|
font-size: 15px;
|
overflow: hidden;
|
text-overflow: ellipsis;
|
white-space: nowrap;
|
flex: 1;
|
}
|
.cond-card__online.online { color: #67c23a; font-size: 18px; }
|
.cond-card__online.offline { color: #909399; font-size: 18px; }
|
.cond-card__uptime {
|
font-size: 12px;
|
color: #a8b4c0;
|
margin-bottom: 10px;
|
}
|
.cond-card__temp {
|
display: flex;
|
align-items: flex-end;
|
gap: 20px;
|
margin-bottom: 12px;
|
}
|
.temp-set .temp-num { font-size: 28px; font-weight: 300; line-height: 1; }
|
.temp-indoor .temp-num.sm { font-size: 20px; color: #b0bec5; }
|
.temp-label { display: block; font-size: 11px; color: #90a4ae; margin-top: 4px; }
|
.cond-card__modes,
|
.cond-card__fans {
|
display: flex;
|
align-items: center;
|
gap: 8px;
|
margin-bottom: 8px;
|
}
|
.cond-card__row-label {
|
font-size: 12px;
|
color: #90a4ae;
|
flex-shrink: 0;
|
min-width: 36px;
|
}
|
.mode-icon,
|
.fan-icon {
|
width: 28px;
|
height: 28px;
|
border-radius: 6px;
|
background: rgba(255, 255, 255, 0.08);
|
display: inline-flex;
|
align-items: center;
|
justify-content: center;
|
font-size: 12px;
|
color: #78909c;
|
cursor: pointer;
|
}
|
.mode-icon.active,
|
.fan-icon.active {
|
background: rgba(64, 158, 255, 0.35);
|
color: #fff;
|
}
|
.mode-icon.active.accent-red,
|
.fan-icon.active.accent-red {
|
background: rgba(245, 108, 108, 0.5);
|
color: #ffecec;
|
box-shadow: 0 0 0 1px rgba(245, 108, 108, 0.55);
|
}
|
.temp-btn {
|
color: #b0bec5;
|
padding: 0 4px;
|
}
|
.cond-card__pwr {
|
font-size: 13px;
|
margin-bottom: 12px;
|
display: flex;
|
align-items: center;
|
gap: 6px;
|
}
|
.pwr-dot {
|
width: 8px;
|
height: 8px;
|
border-radius: 50%;
|
background: #909399;
|
}
|
.pwr-dot.on { background: #67c23a; }
|
.lock-tag {
|
font-size: 11px;
|
color: #e6a23c;
|
border: 1px solid #e6a23c;
|
border-radius: 3px;
|
padding: 0 4px;
|
}
|
.cond-card__actions {
|
display: flex;
|
gap: 6px;
|
flex-wrap: wrap;
|
margin-top: auto;
|
}
|
.cond-card__actions .el-button { flex: 1; min-width: 56px; }
|
.cond-card__meta {
|
margin-top: 8px;
|
font-size: 11px;
|
color: #78909c;
|
display: flex;
|
justify-content: flex-end;
|
gap: 8px;
|
}
|
.wg-block {
|
display: flex;
|
flex-direction: column;
|
align-items: flex-end;
|
max-width: 100%;
|
text-align: right;
|
gap: 2px;
|
}
|
.wg-mac,
|
.wg-bz {
|
overflow: hidden;
|
text-overflow: ellipsis;
|
white-space: nowrap;
|
max-width: 100%;
|
}
|
.wg-bz {
|
color: #90a4ae;
|
font-size: 10px;
|
line-height: 1.2;
|
}
|
</style>
|