446 lines
9.7 KiB
Vue
446 lines
9.7 KiB
Vue
<template>
|
||
<div class="robot-list-modal" v-if="visible">
|
||
<div class="modal-content">
|
||
<!-- 标题 -->
|
||
<div class="modal-header">
|
||
<span class="title">机器人总数</span>
|
||
<img src="../../assets/img/close.png" class="close-icon" @click="handleClose" />
|
||
</div>
|
||
|
||
<!-- 内容区域 -->
|
||
<div class="modal-body">
|
||
<!-- 筛选栏 -->
|
||
<div class="filter-bar">
|
||
<div class="filter-tabs">
|
||
<div
|
||
v-for="tab in tabs"
|
||
:key="tab.type"
|
||
class="tab-item"
|
||
:class="{ active: currentTab === tab.type }"
|
||
@click="handleTabClick(tab.type)"
|
||
>
|
||
{{ tab.label }} ({{ getTabCount(tab.type) }})
|
||
</div>
|
||
</div>
|
||
<div class="type-select">
|
||
<span class="select-label">类型</span>
|
||
<span class="select-divider">|</span>
|
||
<div class="select-wrapper" @click="toggleSelect" @blur="closeSelect" tabindex="0">
|
||
<span class="select-text">{{ selectedType }}</span>
|
||
<img
|
||
v-if="!selectOpen"
|
||
src="../../assets/img/arraw_bottom.png"
|
||
alt="下箭头"
|
||
class="arrow-icon"
|
||
/>
|
||
<img
|
||
v-else
|
||
src="../../assets/img/arraw_top.png"
|
||
alt="上箭头"
|
||
class="arrow-icon"
|
||
/>
|
||
<ul v-if="selectOpen" class="select-dropdown">
|
||
<li v-for="type in robotTypes" :key="type" @click.stop="selectType(type)">{{ type }}</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 表格 -->
|
||
<div class="table-container">
|
||
<table class="robot-table">
|
||
<thead>
|
||
<tr>
|
||
<th>机器人编号</th>
|
||
<th>类型</th>
|
||
<th>状态</th>
|
||
<th>电量</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr v-for="robot in filteredRobots" :key="robot.id">
|
||
<td>{{ robot.id }}</td>
|
||
<td>{{ robot.name }}</td>
|
||
<td :class="robot.status">{{ robot.status }}</td>
|
||
<td>
|
||
<BatteryIndicator :battery="robot.battery" />
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, computed, onMounted, watch } from 'vue';
|
||
import BatteryIndicator from '../common/BatteryIndicator.vue';
|
||
import { homeApi } from '../../api/index';
|
||
|
||
const props = defineProps({
|
||
visible: {
|
||
type: Boolean,
|
||
default: false
|
||
}
|
||
});
|
||
|
||
const emit = defineEmits(['update:visible']);
|
||
|
||
const handleClose = () => {
|
||
emit('update:visible', false);
|
||
};
|
||
|
||
// Tab相关
|
||
const tabs = [
|
||
{ type: 'all', label: '全部' },
|
||
{ type: 'online', label: '在线' },
|
||
{ type: 'offline', label: '离线' },
|
||
{ type: 'fault', label: '故障' }
|
||
];
|
||
|
||
const currentTab = ref('all');
|
||
const handleTabClick = (type) => {
|
||
currentTab.value = type;
|
||
};
|
||
|
||
// 类型选择相关
|
||
const robotTypes = ref(['全部类型']);
|
||
const selectedType = ref('全部类型');
|
||
const selectOpen = ref(false);
|
||
|
||
const toggleSelect = () => {
|
||
selectOpen.value = !selectOpen.value;
|
||
};
|
||
|
||
const closeSelect = () => {
|
||
selectOpen.value = false;
|
||
};
|
||
|
||
const selectType = (type) => {
|
||
selectedType.value = type;
|
||
selectOpen.value = false;
|
||
};
|
||
|
||
// 机器人列表数据
|
||
const robotList = ref([]);
|
||
const loading = ref(false);
|
||
const error = ref(null);
|
||
|
||
// 获取机器人列表
|
||
const fetchRobotList = async () => {
|
||
loading.value = true;
|
||
error.value = null;
|
||
|
||
try {
|
||
const res = await homeApi.getRobotList({
|
||
tenantInfoId: '4fff5d4bcc4b4239941ff077a0da8958', // 租户id
|
||
number: null, // 机器人名
|
||
status: null, // 是否故障
|
||
onlineStatus: null // 在线状态
|
||
});
|
||
|
||
if (res.code === 200) {
|
||
// 处理返回的数据
|
||
const allRobots = [];
|
||
const groupTypes = new Set(['全部类型']);
|
||
|
||
// 遍历分组数据
|
||
Object.entries(res.data).forEach(([groupName, robots]) => {
|
||
robots.forEach(robot => {
|
||
// 添加分组名称
|
||
const robotData = {
|
||
id: robot.number,
|
||
name: groupName,
|
||
groupId: robot.groupingId,
|
||
robotId: robot.robotId,
|
||
status: getStatusText(robot.onlineStatus, robot.status),
|
||
battery: robot.robotInfo?.power || 0,
|
||
position: robot.robotInfo?.positon || '',
|
||
temperature: robot.robotInfo?.temperature || '',
|
||
humidity: robot.robotInfo?.humidity || '',
|
||
updateTime: robot.robotInfo?.updateTime || ''
|
||
};
|
||
|
||
allRobots.push(robotData);
|
||
groupTypes.add(groupName);
|
||
});
|
||
});
|
||
|
||
robotList.value = allRobots;
|
||
robotTypes.value = ['全部类型', ...Array.from(groupTypes).filter(type => type !== '全部类型')];
|
||
|
||
console.log('获取机器人列表成功:', robotList.value);
|
||
} else {
|
||
error.value = res.msg || '获取机器人列表失败';
|
||
console.error('获取机器人列表失败:', res);
|
||
}
|
||
} catch (err) {
|
||
error.value = '获取机器人列表失败';
|
||
console.error('获取机器人列表错误:', err);
|
||
} finally {
|
||
loading.value = false;
|
||
}
|
||
};
|
||
|
||
// 根据状态码获取状态文本
|
||
const getStatusText = (onlineStatus, status) => {
|
||
if (onlineStatus === '0') return '离线';
|
||
|
||
// 在线状态下,根据status判断
|
||
const statusMap = {
|
||
'0': '充电中',
|
||
'1': '待机中',
|
||
'2': '运行中',
|
||
'3': '故障'
|
||
};
|
||
|
||
return statusMap[status] || '未知';
|
||
};
|
||
|
||
// 过滤逻辑
|
||
const filteredRobots = computed(() => {
|
||
let result = robotList.value;
|
||
|
||
// 状态过滤
|
||
if (currentTab.value !== 'all') {
|
||
const statusMap = {
|
||
online: ['运行中', '待机中'],
|
||
offline: ['离线'],
|
||
fault: ['故障']
|
||
};
|
||
|
||
const statusList = statusMap[currentTab.value] || [];
|
||
result = result.filter(robot => statusList.includes(robot.status));
|
||
}
|
||
|
||
// 类型过滤
|
||
if (selectedType.value !== '全部类型') {
|
||
result = result.filter(robot => robot.name === selectedType.value);
|
||
}
|
||
|
||
return result;
|
||
});
|
||
|
||
// 获取各状态数量
|
||
const getTabCount = (type) => {
|
||
if (type === 'all') return robotList.value.length;
|
||
|
||
const statusMap = {
|
||
online: ['运行中', '待机中'],
|
||
offline: ['离线'],
|
||
fault: ['故障']
|
||
};
|
||
|
||
const statusList = statusMap[type] || [];
|
||
return robotList.value.filter(robot => statusList.includes(robot.status)).length;
|
||
};
|
||
|
||
// 监听弹窗显示状态,显示时获取数据
|
||
watch(() => props.visible, (newVal) => {
|
||
if (newVal) {
|
||
fetchRobotList();
|
||
}
|
||
});
|
||
|
||
// 组件挂载时获取数据
|
||
onMounted(() => {
|
||
if (props.visible) {
|
||
fetchRobotList();
|
||
}
|
||
});
|
||
</script>
|
||
|
||
<style scoped>
|
||
.robot-list-modal {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100vw;
|
||
height: 100vh;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
z-index: 1000;
|
||
}
|
||
|
||
.modal-content {
|
||
width: 900px;
|
||
height: 600px;
|
||
background: url("../../assets/img/alert.png") no-repeat;
|
||
background-size: 100% 100%;
|
||
border-radius: 4px;
|
||
padding: 20px;
|
||
}
|
||
|
||
.modal-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.title {
|
||
color: #B9E8FF;
|
||
font-size: 28px;
|
||
letter-spacing: 4px;
|
||
padding-left: 20px;
|
||
}
|
||
|
||
.close-icon {
|
||
width: 20px;
|
||
height: 20px;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.modal-body {
|
||
padding: 0 10px;
|
||
}
|
||
|
||
.filter-bar {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.filter-tabs {
|
||
display: flex;
|
||
gap: 0;
|
||
background: rgba(0, 21, 31, 0.3);
|
||
border: 1px solid rgba(0, 206, 234, 0.7);
|
||
border-radius: 2px;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.tab-item {
|
||
color: #B9E8FF;
|
||
font-size: 14px;
|
||
cursor: pointer;
|
||
padding: 8px 20px;
|
||
border-right: 1px solid rgba(0, 206, 234, 0.7);
|
||
}
|
||
|
||
.tab-item:last-child {
|
||
border-right: none;
|
||
}
|
||
|
||
.tab-item.active {
|
||
background: rgba(0, 206, 234, 0.2);
|
||
}
|
||
|
||
.type-select {
|
||
display: flex;
|
||
align-items: center;
|
||
font-size: 14px;
|
||
color: #B9E8FF;
|
||
background: rgba(0, 21, 31, 0.3);
|
||
border: 1px solid rgba(0, 206, 234, 0.7);
|
||
border-radius: 2px;
|
||
padding: 5px 10px;
|
||
}
|
||
|
||
.select-label {
|
||
color: #B9E8FF;
|
||
}
|
||
|
||
.select-divider {
|
||
color: #00ffff;
|
||
margin: 0 10px;
|
||
}
|
||
|
||
.select-wrapper {
|
||
position: relative;
|
||
cursor: pointer;
|
||
min-width: 120px;
|
||
outline: none;
|
||
}
|
||
|
||
.select-text {
|
||
color: #B9E8FF;
|
||
margin-right: 25px;
|
||
}
|
||
|
||
.arrow-icon {
|
||
position: absolute;
|
||
right: 0;
|
||
top: 50%;
|
||
transform: translateY(-50%);
|
||
width: 16px;
|
||
height: 16px;
|
||
}
|
||
|
||
.select-dropdown {
|
||
position: absolute;
|
||
top: 100%;
|
||
right: 0;
|
||
background: #00273A;
|
||
border: 1px solid rgba(0, 206, 234, 0.7);
|
||
border-radius: 2px;
|
||
margin-top: 5px;
|
||
padding: 5px 0;
|
||
min-width: 120px;
|
||
z-index: 10;
|
||
}
|
||
|
||
.select-dropdown li {
|
||
padding: 8px 15px;
|
||
color: #B9E8FF;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.select-dropdown li:hover {
|
||
background: rgba(0, 206, 234, 0.2);
|
||
}
|
||
|
||
.table-container {
|
||
border: 1px solid rgba(0, 206, 234, 0.7);
|
||
border-radius: 4px;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.robot-table {
|
||
width: 100%;
|
||
border-collapse: collapse;
|
||
color: #B9E8FF;
|
||
}
|
||
|
||
.robot-table th {
|
||
background: rgba(0, 21, 31, 0.3);
|
||
padding: 12px;
|
||
text-align: center;
|
||
font-weight: normal;
|
||
border-bottom: 1px solid #244C60;
|
||
}
|
||
|
||
.robot-table td {
|
||
padding: 12px;
|
||
text-align: center;
|
||
}
|
||
|
||
.robot-table td:last-child {
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
}
|
||
|
||
.robot-table td.运行中 {
|
||
color: #00FF84;
|
||
}
|
||
|
||
.robot-table td.离线 {
|
||
color: #F33F3F;
|
||
}
|
||
|
||
.robot-table td.充电中 {
|
||
color: #FFB800;
|
||
}
|
||
|
||
.battery-wrapper,
|
||
.battery-box,
|
||
.battery-level,
|
||
.battery-text {
|
||
/* 这些样式将被删除 */
|
||
}
|
||
</style> |