feat: 处理冲突
This commit is contained in:
commit
ea9314581e
@ -92,10 +92,10 @@ export function getRobotLocation(robotId) {
|
|||||||
* @param {*} robotId
|
* @param {*} robotId
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export function getRobotStatus(robotId) {
|
export function getRobotStatus(id) {
|
||||||
return request({
|
return request({
|
||||||
url: `/inspection/robot/{robotId}/sync-status`,
|
url: `/inspection/robot/${id}/sync-status`,
|
||||||
method: 'get'
|
method: 'post'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -46,6 +46,29 @@
|
|||||||
</el-tag>
|
</el-tag>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
<el-table-column label="运行状态" width="100" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-tag :type="runtimeStatusType(row.status)" effect="plain">{{ runtimeStatusLabel(row.status) }}</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="电量" width="145">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<div v-if="hasBattery(row)" class="battery-cell">
|
||||||
|
<el-progress :percentage="normalizeBattery(row.batteryLevel)" :stroke-width="8"
|
||||||
|
:color="batteryColor(row.batteryLevel)" />
|
||||||
|
</div>
|
||||||
|
<span v-else class="secondary">尚未获取</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="当前位置" min-width="210" show-overflow-tooltip>
|
||||||
|
<template #default="{ row }">
|
||||||
|
<span v-if="row.currentPosition" class="position-text">{{ positionText(row.currentPosition) }}</span>
|
||||||
|
<span v-else class="secondary">尚未获取AGV位姿</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="当前地图" min-width="135" show-overflow-tooltip>
|
||||||
|
<template #default="{ row }">{{ row.currentMapName || row.robotMapName || '-' }}</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column label="当前端点" min-width="175">
|
<el-table-column label="当前端点" min-width="175">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<span v-if="row.ipAddress && row.port">{{ row.ipAddress }}:{{ row.port }}</span>
|
<span v-if="row.ipAddress && row.port">{{ row.ipAddress }}:{{ row.port }}</span>
|
||||||
@ -69,8 +92,10 @@
|
|||||||
<el-table-column label="类型" prop="robotType" width="120">
|
<el-table-column label="类型" prop="robotType" width="120">
|
||||||
<template #default="{ row }">{{ robotTypeLabel(row.robotType) }}</template>
|
<template #default="{ row }">{{ robotTypeLabel(row.robotType) }}</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="操作" width="220" fixed="right">
|
<el-table-column label="操作" width="245" fixed="right">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
|
<el-button link type="primary" icon="Refresh" :loading="syncingRobotIds.has(row.id)"
|
||||||
|
:disabled="row.connectStatus !== '1'" @click="syncRuntimeState(row)">状态</el-button>
|
||||||
<el-button link type="primary" icon="View" :disabled="!row.robotId" @click="openDevices(row)">设备</el-button>
|
<el-button link type="primary" icon="View" :disabled="!row.robotId" @click="openDevices(row)">设备</el-button>
|
||||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(row)">编辑</el-button>
|
<el-button link type="primary" icon="Edit" @click="handleUpdate(row)">编辑</el-button>
|
||||||
<el-button link type="danger" icon="Delete" @click="handleArchive(row)">归档</el-button>
|
<el-button link type="danger" icon="Delete" @click="handleArchive(row)">归档</el-button>
|
||||||
@ -115,6 +140,8 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="当前端点"><span>{{ endpointText(form) }}</span></el-form-item>
|
<el-form-item label="当前端点"><span>{{ endpointText(form) }}</span></el-form-item>
|
||||||
<el-form-item label="边缘节点"><span>{{ form.quicNodeId || '-' }}</span></el-form-item>
|
<el-form-item label="边缘节点"><span>{{ form.quicNodeId || '-' }}</span></el-form-item>
|
||||||
|
<el-form-item label="AGV电量"><span>{{ hasBattery(form) ? `${normalizeBattery(form.batteryLevel)}%` : '尚未获取' }}</span></el-form-item>
|
||||||
|
<el-form-item label="AGV位姿"><span>{{ form.currentPosition ? positionText(form.currentPosition) : '尚未获取' }}</span></el-form-item>
|
||||||
</template>
|
</template>
|
||||||
</el-form>
|
</el-form>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
@ -154,9 +181,9 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup name="InspectionRobot">
|
<script setup name="InspectionRobot">
|
||||||
import { computed, getCurrentInstance, onBeforeUnmount, reactive, ref } from 'vue'
|
import { computed, getCurrentInstance, onBeforeUnmount, onMounted, reactive, ref } from 'vue'
|
||||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
import { addRobot, deleteRobot, getRobotDevices, getRobotList, updateRobot } from '@/api/inspection/robot'
|
import { addRobot, deleteRobot, getRobotDevices, getRobotList, getRobotStatus, updateRobot } from '@/api/inspection/robot'
|
||||||
import { getMapList } from '@/api/inspection/map'
|
import { getMapList } from '@/api/inspection/map'
|
||||||
|
|
||||||
const { proxy } = getCurrentInstance()
|
const { proxy } = getCurrentInstance()
|
||||||
@ -175,7 +202,9 @@ const devices = ref([])
|
|||||||
const deviceKeyword = ref('')
|
const deviceKeyword = ref('')
|
||||||
const deviceOnline = ref('')
|
const deviceOnline = ref('')
|
||||||
const activeRobot = ref({})
|
const activeRobot = ref({})
|
||||||
let refreshTimer
|
const syncingRobotIds = reactive(new Set())
|
||||||
|
let deviceRefreshTimer
|
||||||
|
let listRefreshTimer
|
||||||
|
|
||||||
const queryParams = reactive({ pageNum: 1, pageSize: 10, robotId: '', robotName: '', connectStatus: '' })
|
const queryParams = reactive({ pageNum: 1, pageSize: 10, robotId: '', robotName: '', connectStatus: '' })
|
||||||
const emptyForm = () => ({ id: '', robotId: '', robotName: '', robotCode: '', robotModel: '', robotType: '', currentMapId: '' })
|
const emptyForm = () => ({ id: '', robotId: '', robotName: '', robotCode: '', robotModel: '', robotType: '', currentMapId: '' })
|
||||||
@ -193,9 +222,9 @@ const filteredDevices = computed(() => devices.value.filter(item => {
|
|||||||
return (!keyword || text.includes(keyword)) && (!deviceOnline.value || item.onlineStatus === deviceOnline.value)
|
return (!keyword || text.includes(keyword)) && (!deviceOnline.value || item.onlineStatus === deviceOnline.value)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
async function getList() {
|
async function getList(showLoading = true) {
|
||||||
loading.value = true
|
if (showLoading) loading.value = true
|
||||||
try { const res = await getRobotList(queryParams); robotList.value = res.rows || []; total.value = res.total || 0 } finally { loading.value = false }
|
try { const res = await getRobotList(queryParams); robotList.value = res.rows || []; total.value = res.total || 0 } finally { if (showLoading) loading.value = false }
|
||||||
}
|
}
|
||||||
function handleQuery() { queryParams.pageNum = 1; getList() }
|
function handleQuery() { queryParams.pageNum = 1; getList() }
|
||||||
function resetQuery() { Object.assign(queryParams, { pageNum: 1, robotId: '', robotName: '', connectStatus: '' }); getList() }
|
function resetQuery() { Object.assign(queryParams, { pageNum: 1, robotId: '', robotName: '', connectStatus: '' }); getList() }
|
||||||
@ -219,7 +248,7 @@ async function handleArchive(row) {
|
|||||||
}
|
}
|
||||||
async function openDevices(row) {
|
async function openDevices(row) {
|
||||||
activeRobot.value = row; deviceOpen.value = true; deviceKeyword.value = ''; deviceOnline.value = ''; await loadDevices()
|
activeRobot.value = row; deviceOpen.value = true; deviceKeyword.value = ''; deviceOnline.value = ''; await loadDevices()
|
||||||
clearInterval(refreshTimer); refreshTimer = setInterval(() => { if (deviceOpen.value) loadDevices(false) }, 5000)
|
clearInterval(deviceRefreshTimer); deviceRefreshTimer = setInterval(() => { if (deviceOpen.value) loadDevices(false) }, 5000)
|
||||||
}
|
}
|
||||||
async function loadDevices(showLoading = true) {
|
async function loadDevices(showLoading = true) {
|
||||||
if (!activeRobot.value.id) return
|
if (!activeRobot.value.id) return
|
||||||
@ -227,6 +256,33 @@ async function loadDevices(showLoading = true) {
|
|||||||
try { const res = await getRobotDevices(activeRobot.value.id); devices.value = res.data || [] } finally { deviceLoading.value = false }
|
try { const res = await getRobotDevices(activeRobot.value.id); devices.value = res.data || [] } finally { deviceLoading.value = false }
|
||||||
}
|
}
|
||||||
function endpointText(row) { return row.ipAddress && row.port ? `${row.ipAddress}:${row.port}` : '等待连接后自动获取' }
|
function endpointText(row) { return row.ipAddress && row.port ? `${row.ipAddress}:${row.port}` : '等待连接后自动获取' }
|
||||||
|
function runtimeStatusLabel(value) { return ({ 0: '在线', 1: '离线', 2: '充电中', 3: '巡检中' })[value] || '未知' }
|
||||||
|
function runtimeStatusType(value) { return ({ 0: 'success', 1: 'info', 2: 'warning', 3: 'primary' })[value] || 'info' }
|
||||||
|
function hasBattery(row) { return row.batteryLevel !== null && row.batteryLevel !== undefined && row.batteryLevel !== '' }
|
||||||
|
function normalizeBattery(value) { return Math.max(0, Math.min(100, Math.round(Number(value) || 0))) }
|
||||||
|
function batteryColor(value) {
|
||||||
|
const level = normalizeBattery(value)
|
||||||
|
if (level <= 20) return '#f56c6c'
|
||||||
|
if (level <= 40) return '#e6a23c'
|
||||||
|
return '#36a269'
|
||||||
|
}
|
||||||
|
function positionText(value) {
|
||||||
|
const values = String(value || '').split(',').map(item => Number(item))
|
||||||
|
if (values.length < 2 || values.some(item => !Number.isFinite(item))) return value || '-'
|
||||||
|
const theta = values.length > 2 ? `,θ ${values[2].toFixed(2)}` : ''
|
||||||
|
return `X ${values[0].toFixed(2)},Y ${values[1].toFixed(2)}${theta}`
|
||||||
|
}
|
||||||
|
async function syncRuntimeState(row) {
|
||||||
|
if (!row.id || syncingRobotIds.has(row.id)) return
|
||||||
|
syncingRobotIds.add(row.id)
|
||||||
|
try {
|
||||||
|
const res = await getRobotStatus(row.id)
|
||||||
|
if (res.data) Object.assign(row, res.data)
|
||||||
|
ElMessage.success('机器人运行状态已刷新')
|
||||||
|
} finally {
|
||||||
|
syncingRobotIds.delete(row.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
function robotTypeLabel(value) { return robot_type.value?.find(item => String(item.value) === String(value))?.label || value || '-' }
|
function robotTypeLabel(value) { return robot_type.value?.find(item => String(item.value) === String(value))?.label || value || '-' }
|
||||||
const kindNames = { 1: 'AGV', 2: '机械臂', 3: '电池', 4: '仿生头', 5: '摄像头', 6: 'CAN总线', 7: '灵巧手', 8: '夹爪', 9: '麦克风', 10: '电机', 11: '电机系统', 12: '机器人', 13: '扬声器' }
|
const kindNames = { 1: 'AGV', 2: '机械臂', 3: '电池', 4: '仿生头', 5: '摄像头', 6: 'CAN总线', 7: '灵巧手', 8: '夹爪', 9: '麦克风', 10: '电机', 11: '电机系统', 12: '机器人', 13: '扬声器' }
|
||||||
const stateNames = { 1: '停用', 2: '初始化', 3: '已注册', 4: '就绪', 5: '运行中', 6: '已停止', 7: '错误' }
|
const stateNames = { 1: '停用', 2: '初始化', 3: '已注册', 4: '就绪', 5: '运行中', 6: '已停止', 7: '错误' }
|
||||||
@ -235,9 +291,19 @@ function managerStateLabel(value) { return stateNames[value] || '-' }
|
|||||||
function healthLabel(row) { if (row.hasError === '1') return '故障'; return ({ 1: '健康', 2: '降级', 3: '故障' })[row.healthStatus] || '未知' }
|
function healthLabel(row) { if (row.hasError === '1') return '故障'; return ({ 1: '健康', 2: '降级', 3: '故障' })[row.healthStatus] || '未知' }
|
||||||
function healthTag(row) { return row.hasError === '1' || row.healthStatus === 3 ? 'danger' : row.healthStatus === 2 ? 'warning' : row.healthStatus === 1 ? 'success' : 'info' }
|
function healthTag(row) { return row.hasError === '1' || row.healthStatus === 3 ? 'danger' : row.healthStatus === 2 ? 'warning' : row.healthStatus === 1 ? 'success' : 'info' }
|
||||||
|
|
||||||
onBeforeUnmount(() => clearInterval(refreshTimer))
|
onMounted(() => {
|
||||||
getList()
|
getList()
|
||||||
getMapList({ pageNum: 1, pageSize: 10000 }).then(res => { mapList.value = (res.rows || []).map(item => ({ label: item.mapName, value: item.id })) })
|
listRefreshTimer = setInterval(() => {
|
||||||
|
if (!document.hidden && !formOpen.value) getList(false)
|
||||||
|
}, 5000)
|
||||||
|
getMapList({ pageNum: 1, pageSize: 10000 }).then(res => {
|
||||||
|
mapList.value = (res.rows || []).map(item => ({ label: item.mapName, value: item.id }))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
clearInterval(deviceRefreshTimer)
|
||||||
|
clearInterval(listRefreshTimer)
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@ -247,6 +313,9 @@ getMapList({ pageNum: 1, pageSize: 10000 }).then(res => { mapList.value = (res.r
|
|||||||
.drawer-title { font-size: 16px; margin-bottom: 4px; }
|
.drawer-title { font-size: 16px; margin-bottom: 4px; }
|
||||||
.secondary { color: var(--el-text-color-secondary); font-size: 12px; line-height: 20px; }
|
.secondary { color: var(--el-text-color-secondary); font-size: 12px; line-height: 20px; }
|
||||||
.identity-text { font-family: ui-monospace, SFMono-Regular, Consolas, monospace; }
|
.identity-text { font-family: ui-monospace, SFMono-Regular, Consolas, monospace; }
|
||||||
|
.battery-cell { width: 118px; }
|
||||||
|
.battery-cell :deep(.el-progress__text) { min-width: 34px; font-size: 12px !important; }
|
||||||
|
.position-text { font-family: ui-monospace, SFMono-Regular, Consolas, monospace; font-size: 12px; }
|
||||||
.fault-tag { margin-left: 6px; }
|
.fault-tag { margin-left: 6px; }
|
||||||
.robot-form { margin-top: 22px; }
|
.robot-form { margin-top: 22px; }
|
||||||
.device-toolbar { display: grid; grid-template-columns: minmax(220px, 1fr) 150px auto; gap: 10px; margin-bottom: 16px; }
|
.device-toolbar { display: grid; grid-template-columns: minmax(220px, 1fr) 150px auto; gap: 10px; margin-bottom: 16px; }
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user