l3能源成本分摊(部分完成留存)

This commit is contained in:
2025-12-07 17:23:47 +08:00
parent b6328a94da
commit 59951b77c3
100 changed files with 14350 additions and 847 deletions

View 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>