l3能源成本分摊(部分完成留存)
This commit is contained in:
257
klp-ui/src/views/ems/meter/components/LocationTreeNode.vue
Normal file
257
klp-ui/src/views/ems/meter/components/LocationTreeNode.vue
Normal file
@@ -0,0 +1,257 @@
|
||||
<template>
|
||||
<div class="location-tree-node">
|
||||
<!-- 节点头部 -->
|
||||
<div class="node-header" :class="{ expanded: isExpanded }">
|
||||
<div class="node-title">
|
||||
<span
|
||||
v-if="node.children && node.children.length > 0"
|
||||
class="expand-icon"
|
||||
@click="toggleExpand"
|
||||
>
|
||||
{{ isExpanded ? '▼' : '▶' }}
|
||||
</span>
|
||||
<span v-else class="expand-icon-placeholder" />
|
||||
|
||||
<span class="location-name">{{ node.name }}</span>
|
||||
<el-tag type="info" size="small">{{ getDeviceCount(node.locationId) }}个设备</el-tag>
|
||||
</div>
|
||||
|
||||
<div class="node-actions">
|
||||
<el-button type="text" size="small" @click="$emit('edit', node)">编辑</el-button>
|
||||
<el-button type="text" size="small" @click="$emit('add-device', node)">添加设备</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 设备列表 -->
|
||||
<div v-if="isExpanded" class="device-list">
|
||||
<div
|
||||
v-for="device in getLocationDevices(node.locationId)"
|
||||
:key="device.meterId"
|
||||
class="device-item"
|
||||
@click="$emit('device-click', device)"
|
||||
>
|
||||
<div class="device-info">
|
||||
<span class="device-code">{{ device.meterCode }}</span>
|
||||
<span class="device-energy">{{ getEnergyName(device.energyTypeId) }}</span>
|
||||
<el-tag
|
||||
:type="getStatusType(device.status)"
|
||||
size="small"
|
||||
>
|
||||
{{ getStatusLabel(device.status) }}
|
||||
</el-tag>
|
||||
</div>
|
||||
<div class="device-location">
|
||||
{{ device.model }} / {{ device.manufacturer }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="getLocationDevices(node.locationId).length === 0" class="no-devices">
|
||||
暂无设备
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 子节点 -->
|
||||
<div v-if="isExpanded && node.children && node.children.length > 0" class="children-nodes">
|
||||
<location-tree-node
|
||||
v-for="child in node.children"
|
||||
:key="child.locationId"
|
||||
:node="child"
|
||||
:meter-list="meterList"
|
||||
:energy-type-list="energyTypeList"
|
||||
@edit="$emit('edit', $event)"
|
||||
@add-device="$emit('add-device', $event)"
|
||||
@device-click="$emit('device-click', $event)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'LocationTreeNode',
|
||||
props: {
|
||||
node: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
meterList: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
energyTypeList: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
emits: ['edit', 'add-device', 'device-click'],
|
||||
data() {
|
||||
return {
|
||||
isExpanded: false
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
toggleExpand() {
|
||||
this.isExpanded = !this.isExpanded;
|
||||
},
|
||||
|
||||
getLocationDevices(locationId) {
|
||||
return this.meterList.filter(m => m.locationId === locationId);
|
||||
},
|
||||
|
||||
getDeviceCount(locationId) {
|
||||
return this.getLocationDevices(locationId).length;
|
||||
},
|
||||
|
||||
getEnergyName(energyTypeId) {
|
||||
const energy = this.energyTypeList.find(item => item.energyTypeId === energyTypeId);
|
||||
return energy ? energy.name : '-';
|
||||
},
|
||||
|
||||
getStatusLabel(status) {
|
||||
const statusMap = {
|
||||
0: '在用',
|
||||
1: '停用',
|
||||
2: '维护'
|
||||
};
|
||||
return statusMap[status] || '未知';
|
||||
},
|
||||
|
||||
getStatusType(status) {
|
||||
const typeMap = {
|
||||
0: 'success',
|
||||
1: 'info',
|
||||
2: 'warning'
|
||||
};
|
||||
return typeMap[status] || 'info';
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.location-tree-node {
|
||||
margin-bottom: 8px;
|
||||
border: 1px solid #ebeef5;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
|
||||
.node-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 12px 16px;
|
||||
background: #f5f7fa;
|
||||
cursor: pointer;
|
||||
transition: background 0.3s ease;
|
||||
|
||||
&:hover {
|
||||
background: #e8f4f8;
|
||||
}
|
||||
|
||||
&.expanded {
|
||||
background: #e8f4f8;
|
||||
}
|
||||
|
||||
.node-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
flex: 1;
|
||||
|
||||
.expand-icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
cursor: pointer;
|
||||
transition: transform 0.3s ease;
|
||||
color: #409eff;
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.2);
|
||||
}
|
||||
}
|
||||
|
||||
.expand-icon-placeholder {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.location-name {
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.node-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.device-list {
|
||||
padding: 12px 16px;
|
||||
background: #fafafa;
|
||||
border-top: 1px solid #ebeef5;
|
||||
|
||||
.device-item {
|
||||
padding: 10px 12px;
|
||||
background: #ffffff;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 8px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
border: 1px solid #e8e8e8;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-color: #409eff;
|
||||
box-shadow: 0 2px 8px rgba(64, 158, 255, 0.1);
|
||||
transform: translateX(4px);
|
||||
}
|
||||
|
||||
.device-info {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
margin-bottom: 4px;
|
||||
|
||||
.device-code {
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.device-energy {
|
||||
color: #909399;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.device-location {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
padding-left: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.no-devices {
|
||||
text-align: center;
|
||||
color: #909399;
|
||||
padding: 20px 0;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.children-nodes {
|
||||
padding: 12px 16px;
|
||||
background: #fafafa;
|
||||
border-top: 1px solid #ebeef5;
|
||||
margin-left: 16px;
|
||||
border-left: 2px solid #d9d9d9;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user