feat(flow): 将流程节点中的终端ID替换为机器人ID
- 替换singleNodeTerminalId为singleNodeRobotId - 更新流程图中机器人选择功能,使用机器人ID替代终端ID - 修改AGV移动到站点功能以使用机器人ID - 更新任务执行页面中的机器人选择组件 - 重构机器人管理页面,添加设备管理功能 - 实现机器人详情抽屉和设备列表展示 - 添加机器人归档功能和在线状态显示
This commit is contained in:
parent
f4eb49763a
commit
210e4cf716
@ -31,6 +31,13 @@ export function deleteRobot(id) {
|
||||
})
|
||||
}
|
||||
|
||||
export function getRobotDevices(id) {
|
||||
return request({
|
||||
url: `/inspection/robot/${id}/devices`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取机器人地图
|
||||
* @param {*} robotId
|
||||
|
||||
@ -30,7 +30,7 @@
|
||||
</el-table-column>
|
||||
<el-table-column prop="value" label="入参值">
|
||||
<template #default="{ row, column, $index }">
|
||||
<el-form-item v-if="row.name === 'terminalId'" label="" :prop="`tableData${row.propPath}value`" :rules="row.required ? [{ required: true, message: '请输入变量名', trigger: 'blur' }] : []">
|
||||
<el-form-item v-if="row.name === 'terminalId' || row.name === 'robotId'" label="" :prop="`tableData${row.propPath}value`" :rules="row.required ? [{ required: true, message: '请选择执行机器人', trigger: 'change' }] : []">
|
||||
<el-select v-model="row.value" style="width: 200px">
|
||||
<el-option
|
||||
v-for="item in terminalIdOptions"
|
||||
@ -66,7 +66,7 @@ import { getStartNodeFormData, formatTableData } from '@/utils/flow'
|
||||
import { flowExecuteTrial } from '@/api/device/flow'
|
||||
import { emitter } from '@/utils/eventBus';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import {listTerminal} from "@/api/device/terminal.js";
|
||||
import { getRobotList } from '@/api/inspection/robot'
|
||||
|
||||
const props = defineProps({
|
||||
drawer: Boolean,
|
||||
@ -104,11 +104,11 @@ const confirm = () => {
|
||||
})
|
||||
|
||||
const flowData = JSON.stringify(lf.getGraphData())
|
||||
const { terminalId, ...runParams } = formData
|
||||
const { terminalId, robotId, ...runParams } = formData
|
||||
const res = await flowExecuteTrial({
|
||||
flowData,
|
||||
itemId: props.flowId,
|
||||
terminalId,
|
||||
robotId: robotId || terminalId,
|
||||
runParams
|
||||
})
|
||||
if (res.code === 200) {
|
||||
@ -127,12 +127,12 @@ const terminalIdOptions = ref([])
|
||||
|
||||
/** 获取设备终端列表 */
|
||||
const handleGetTerminalGroup = async (row) => {
|
||||
const res = await listTerminal({ pageNum: 1, pageSize: 1000 })
|
||||
const res = await getRobotList({ pageNum: 1, pageSize: 1000, connectStatus: '1' })
|
||||
if (res.code === 200) {
|
||||
terminalIdOptions.value = res.rows?.map((item) => {
|
||||
return {
|
||||
label: `${item.name}(${item.host}:${item.port})`,
|
||||
value: item.id
|
||||
label: `${item.robotName || item.robotId} (${item.robotId})`,
|
||||
value: item.robotId
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -166,10 +166,10 @@
|
||||
type="primary"
|
||||
plain
|
||||
:loading="stationLoading"
|
||||
:disabled="!singleNodeTerminalId || !singleNodeDeviceId"
|
||||
:disabled="!singleNodeRobotId || !singleNodeDeviceId"
|
||||
@click="loadSingleNodeStations"
|
||||
>加载站点列表</el-button>
|
||||
<span>{{ singleNodeTerminalId && singleNodeDeviceId ? '加载后从站点下拉框选择' : '请先填写终端ID和设备ID' }}</span>
|
||||
<span>{{ singleNodeRobotId && singleNodeDeviceId ? '加载后从站点下拉框选择' : '请先选择机器人和设备' }}</span>
|
||||
</div>
|
||||
<el-form
|
||||
:inline="true"
|
||||
@ -269,6 +269,7 @@ import {
|
||||
getAgvStations,
|
||||
} from "@/api/device/flow";
|
||||
import { getDetect } from "@/api/test/detect";
|
||||
import { getRobotList } from '@/api/inspection/robot'
|
||||
import {
|
||||
CircleClose,
|
||||
CollectionTag,
|
||||
@ -890,8 +891,9 @@ const rules = reactive({});
|
||||
const executeForm = ref();
|
||||
const nodeAction = ref("");
|
||||
const stationLoading = ref(false);
|
||||
const singleNodeTerminalId = computed(() =>
|
||||
formData.nodeParams.find((item) => item.name === "terminalId")?.input
|
||||
const singleNodeRobotOptions = ref([]);
|
||||
const singleNodeRobotId = computed(() =>
|
||||
formData.nodeParams.find((item) => item.name === "robotId")?.input
|
||||
);
|
||||
const singleNodeDeviceId = computed(() =>
|
||||
formData.nodeParams.find((item) => item.name === "deviceId")?.input
|
||||
@ -909,14 +911,14 @@ const loadSingleNodeStations = async () => {
|
||||
stationLoading.value = true;
|
||||
try {
|
||||
const response = await getAgvStations({
|
||||
terminalId: singleNodeTerminalId.value,
|
||||
robotId: singleNodeRobotId.value,
|
||||
deviceId: singleNodeDeviceId.value,
|
||||
});
|
||||
const options = stationOptions(response?.data);
|
||||
const stationParam = formData.nodeParams.find((item) => item.name === "stationId");
|
||||
if (!stationParam) return;
|
||||
stationParam.selectOptions = options;
|
||||
stationParam.stationTerminalId = singleNodeTerminalId.value;
|
||||
stationParam.stationRobotId = singleNodeRobotId.value;
|
||||
stationParam.stationDeviceId = singleNodeDeviceId.value;
|
||||
if (!options.some((item) => item.value === stationParam.input)) stationParam.input = "";
|
||||
if (options.length === 0) ElMessage.warning("当前AGV没有可用站点");
|
||||
@ -928,7 +930,15 @@ const loadSingleNodeStations = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
const singleNodeExecution = (e) => {
|
||||
const loadSingleNodeRobots = async () => {
|
||||
const response = await getRobotList({ pageNum: 1, pageSize: 1000, connectStatus: '1' });
|
||||
singleNodeRobotOptions.value = (response.rows || []).map(item => ({
|
||||
label: `${item.robotName || item.robotId} (${item.robotId})`,
|
||||
value: item.robotId,
|
||||
}));
|
||||
};
|
||||
|
||||
const singleNodeExecution = async (e) => {
|
||||
const { name, nodeType, nodeParams, action } = e.detail;
|
||||
const executionParams = JSON.parse(JSON.stringify(nodeParams || []));
|
||||
nodeName.value = name;
|
||||
@ -936,12 +946,15 @@ const singleNodeExecution = (e) => {
|
||||
nodeAction.value = action;
|
||||
stationLoading.value = false;
|
||||
if (nodeType === "EDGE") {
|
||||
await loadSingleNodeRobots();
|
||||
formData.nodeParams = [
|
||||
{
|
||||
disabled: true,
|
||||
input: "",
|
||||
name: "terminalId",
|
||||
name: "robotId",
|
||||
type: "input",
|
||||
componentType: "select",
|
||||
selectOptions: singleNodeRobotOptions.value,
|
||||
},
|
||||
...executionParams,
|
||||
];
|
||||
@ -961,7 +974,7 @@ const execute = async () => {
|
||||
if (nodeAction.value === "AGV_MOVE_TO_STATION") {
|
||||
const stationParam = formData.nodeParams.find((item) => item.name === "stationId");
|
||||
const validStation = stationParam?.selectOptions?.some((item) => item.value === stationParam.input);
|
||||
const correctSource = stationParam?.stationTerminalId === singleNodeTerminalId.value
|
||||
const correctSource = stationParam?.stationRobotId === singleNodeRobotId.value
|
||||
&& stationParam?.stationDeviceId === singleNodeDeviceId.value;
|
||||
if (!validStation || !correctSource) {
|
||||
ElMessage.warning("请按当前终端和设备重新加载并选择站点");
|
||||
|
||||
@ -1,335 +1,254 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<div ref="topContainerRef">
|
||||
<el-form :model="queryParams" :inline="true" v-show="showSearch" label-width="100px">
|
||||
<el-form-item label="机器人名称" prop="name">
|
||||
<el-input v-model="queryParams.name" placeholder="请输入机器人名称" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="IP地址" prop="ipAddress">
|
||||
<el-input v-model="queryParams.ipAddress" placeholder="请输入IP地址" clearable
|
||||
@keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div class="app-container robot-page">
|
||||
<el-form v-show="showSearch" :model="queryParams" inline class="query-form">
|
||||
<el-form-item label="机器人ID">
|
||||
<el-input v-model="queryParams.robotId" placeholder="永久身份ID" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="名称">
|
||||
<el-input v-model="queryParams.robotName" placeholder="机器人名称" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="连接状态">
|
||||
<el-select v-model="queryParams.connectStatus" placeholder="全部" clearable style="width: 120px">
|
||||
<el-option label="在线" value="1" />
|
||||
<el-option label="离线" value="0" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd">新增</el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd">新增机器人</el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
|
||||
</el-row>
|
||||
|
||||
<el-table v-loading="loading" :data="robotList" row-key="id">
|
||||
<el-table-column label="机器人" min-width="190">
|
||||
<template #default="{ row }">
|
||||
<div class="robot-name">{{ row.robotName || '未命名机器人' }}</div>
|
||||
<div class="secondary">{{ row.robotCode || '未设置业务编号' }}</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="永久机器人ID" prop="robotId" min-width="210" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.robotId" class="identity-text">{{ row.robotId }}</span>
|
||||
<el-tag v-else type="info" effect="plain">旧数据</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="连接" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.connectStatus === '1' ? 'success' : 'info'" effect="light">
|
||||
{{ row.connectStatus === '1' ? '在线' : '等待接入' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="当前端点" min-width="175">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.ipAddress && row.port">{{ row.ipAddress }}:{{ row.port }}</span>
|
||||
<span v-else class="secondary">连接后自动获取</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="设备" width="150" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-button v-if="row.robotId" link type="primary" @click="openDevices(row)">
|
||||
{{ row.onlineDeviceCount || 0 }}/{{ row.deviceTotal || 0 }} 在线
|
||||
</el-button>
|
||||
<span v-else>-</span>
|
||||
<el-tag v-if="row.faultDeviceCount" type="danger" size="small" class="fault-tag">
|
||||
{{ row.faultDeviceCount }} 故障
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="最后心跳" prop="lastHeartbeatTime" width="175">
|
||||
<template #default="{ row }">{{ row.lastHeartbeatTime || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="类型" prop="robotType" width="120">
|
||||
<template #default="{ row }">{{ robotTypeLabel(row.robotType) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="180" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<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="danger" icon="Delete" @click="handleArchive(row)">归档</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum"
|
||||
v-model:limit="queryParams.pageSize" @pagination="getList" />
|
||||
|
||||
<el-dialog v-model="formOpen" :title="form.id ? '编辑机器人' : '登记机器人'" width="620px" append-to-body>
|
||||
<el-alert v-if="!form.id" title="登记永久机器人ID后,IP、端口和设备将在QUIC连接时自动同步。" type="info" show-icon :closable="false" />
|
||||
<el-form ref="robotRef" :model="form" :rules="rules" label-width="110px" class="robot-form">
|
||||
<el-form-item label="机器人ID" prop="robotId">
|
||||
<el-input v-model="form.robotId" :disabled="Boolean(form.id)" maxlength="128" placeholder="与边缘端 robot_id 完全一致">
|
||||
<template #prefix><el-icon><Lock /></el-icon></template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="机器人名称" prop="robotName">
|
||||
<el-input v-model="form.robotName" placeholder="请输入机器人名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="业务编号" prop="robotCode">
|
||||
<el-input v-model="form.robotCode" placeholder="用于业务展示,可后续修改" />
|
||||
</el-form-item>
|
||||
<el-form-item label="机器人型号">
|
||||
<el-input v-model="form.robotModel" placeholder="选填" />
|
||||
</el-form-item>
|
||||
<el-form-item label="机器人类型" prop="robotType">
|
||||
<el-select v-model="form.robotType" placeholder="请选择" clearable style="width: 100%">
|
||||
<el-option v-for="type in robot_type" :key="type.value" :label="type.label" :value="type.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="当前地图">
|
||||
<el-select v-model="form.currentMapId" placeholder="可稍后绑定" clearable style="width: 100%">
|
||||
<el-option v-for="map in mapList" :key="map.value" :label="map.label" :value="map.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<template v-if="form.id">
|
||||
<el-divider content-position="left">连接信息</el-divider>
|
||||
<el-form-item label="连接状态">
|
||||
<el-tag :type="form.connectStatus === '1' ? 'success' : 'info'">{{ form.connectStatus === '1' ? '在线' : '等待接入' }}</el-tag>
|
||||
</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>
|
||||
</template>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="formOpen = false">取消</el-button>
|
||||
<el-button type="primary" :loading="submitting" @click="submitForm">保存</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-drawer v-model="deviceOpen" size="min(960px, 92vw)" destroy-on-close>
|
||||
<template #header>
|
||||
<div>
|
||||
<div class="drawer-title">{{ activeRobot.robotName || activeRobot.robotId }}</div>
|
||||
<div class="secondary">{{ activeRobot.robotId }} · {{ endpointText(activeRobot) }}</div>
|
||||
</div>
|
||||
|
||||
<div :style="containerHeight">
|
||||
<el-table v-loading="loading" height="100%" style="width: 100%" :data="tableDataList">
|
||||
<el-table-column label="机器人名称" align="center" prop="robotName" />
|
||||
<el-table-column label="机器人编码" align="center" prop="robotCode" />
|
||||
<el-table-column label="机器人类型" align="center" prop="robotType">
|
||||
<template #default="scope">
|
||||
{{ scope.row.robotType == 1 ? '讲解机器人' : '巡检机器人' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="IP地址" align="center" prop="ipAddress" min-width="160" show-overflow-tooltip />
|
||||
<el-table-column label="电量" align="center" prop="batteryLevel" />
|
||||
<el-table-column label="状态" align="center" prop="status" />
|
||||
<el-table-column label="当前地图" align="center" prop="currentMapName" />
|
||||
<el-table-column label="操作" class-name="small-padding fixed-width" width="200" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)">修改</el-button>
|
||||
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum"
|
||||
v-model:limit="queryParams.pageSize" @pagination="getList" />
|
||||
</div>
|
||||
|
||||
<!-- 添加或修改语料库对话框 -->
|
||||
<el-dialog :title="title" v-model="open" width="580px" append-to-body>
|
||||
<el-form ref="robotRef" :model="form" :rules="rules" label-width="100px">
|
||||
<el-form-item label="机器人名称" prop="robotName">
|
||||
<el-input v-model="form.robotName" placeholder="请输入机器人名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="机器人编码" prop="robotCode">
|
||||
<el-input v-model="form.robotCode" placeholder="请输入机器人编码" />
|
||||
</el-form-item>
|
||||
<el-form-item label="机器人类型" prop="robotType">
|
||||
<el-select v-model="form.robotType" placeholder="请输入机器人类型" clearable style="width: 100%">
|
||||
<el-option v-for="type in robot_type" :key="type.value" :label="type.label"
|
||||
:value="type.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="IP地址" prop="ipAddress">
|
||||
<el-input class="test" v-model="form.ipAddress" placeholder="请输入IP地址" @input="ipChange">
|
||||
<template #append>
|
||||
<div class="connection" :class="{ success: connectionStatus }" @click="testConnection">
|
||||
{{ connectionStatus ? '连接成功' : '测试连接' }}
|
||||
</div>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="端口号" prop="port">
|
||||
<el-input v-model="form.port" placeholder="请输入端口号" />
|
||||
</el-form-item>
|
||||
<el-form-item label="地图选择" prop="currentMapId">
|
||||
<el-select v-model="form.currentMapId" placeholder="请选择地图" clearable style="width: 100%">
|
||||
<el-option v-for="map in mapList" :key="map.value" :label="map.label" :value="map.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
<div class="device-toolbar">
|
||||
<el-input v-model="deviceKeyword" placeholder="搜索设备ID或类型" clearable prefix-icon="Search" />
|
||||
<el-select v-model="deviceOnline" placeholder="全部状态" clearable>
|
||||
<el-option label="在线" value="1" /><el-option label="离线" value="0" />
|
||||
</el-select>
|
||||
<el-button icon="Refresh" :loading="deviceLoading" @click="loadDevices">刷新</el-button>
|
||||
</div>
|
||||
<el-table v-loading="deviceLoading" :data="filteredDevices" empty-text="机器人尚未上报设备">
|
||||
<el-table-column label="设备" min-width="180">
|
||||
<template #default="{ row }"><strong>{{ row.deviceName || row.deviceId }}</strong><div class="secondary">{{ row.deviceId }}</div></template>
|
||||
</el-table-column>
|
||||
<el-table-column label="类型" width="120"><template #default="{ row }">{{ deviceKindLabel(row.deviceKind) }}</template></el-table-column>
|
||||
<el-table-column label="实现" prop="typeName" min-width="150" show-overflow-tooltip />
|
||||
<el-table-column label="运行状态" width="110"><template #default="{ row }">{{ managerStateLabel(row.managerState) }}</template></el-table-column>
|
||||
<el-table-column label="健康" width="100"><template #default="{ row }"><el-tag :type="healthTag(row)">{{ healthLabel(row) }}</el-tag></template></el-table-column>
|
||||
<el-table-column label="在线" width="90"><template #default="{ row }"><el-tag :type="row.onlineStatus === '1' ? 'success' : 'info'">{{ row.onlineStatus === '1' ? '在线' : '离线' }}</el-tag></template></el-table-column>
|
||||
<el-table-column label="最后发现" prop="lastSeenTime" width="175" />
|
||||
<el-table-column label="错误" prop="errorMessage" min-width="180" show-overflow-tooltip />
|
||||
</el-table>
|
||||
</el-drawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="Corpus">
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import { useContainerHeight } from "@/hooks/tableHeight";
|
||||
import { addRobot, updateRobot, deleteRobot, getRobotList } from "../../../api/inspection/robot";
|
||||
import { getMapList } from "../../../api/inspection/map";
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
const { robot_type } = proxy.useDict(
|
||||
"robot_type"
|
||||
);
|
||||
|
||||
const robotRef = ref();
|
||||
|
||||
const topContainerRef = ref();
|
||||
const containerHeight = useContainerHeight(topContainerRef);
|
||||
|
||||
const tableDataList = ref([]);
|
||||
const open = ref(false);
|
||||
const loading = ref(true);
|
||||
const showSearch = ref(true);
|
||||
|
||||
const total = ref(0);
|
||||
const title = ref("");
|
||||
const isNew = ref(true);
|
||||
<script setup name="InspectionRobot">
|
||||
import { computed, getCurrentInstance, onBeforeUnmount, reactive, ref } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { addRobot, deleteRobot, getRobotDevices, getRobotList, updateRobot } from '@/api/inspection/robot'
|
||||
import { getMapList } from '@/api/inspection/map'
|
||||
|
||||
const { proxy } = getCurrentInstance()
|
||||
const { robot_type } = proxy.useDict('robot_type')
|
||||
const robotRef = ref()
|
||||
const loading = ref(false)
|
||||
const submitting = ref(false)
|
||||
const showSearch = ref(true)
|
||||
const total = ref(0)
|
||||
const robotList = ref([])
|
||||
const mapList = ref([])
|
||||
const formOpen = ref(false)
|
||||
const deviceOpen = ref(false)
|
||||
const deviceLoading = ref(false)
|
||||
const devices = ref([])
|
||||
const deviceKeyword = ref('')
|
||||
const deviceOnline = ref('')
|
||||
const activeRobot = ref({})
|
||||
let refreshTimer
|
||||
|
||||
const data = reactive({
|
||||
form: {
|
||||
robotName: "",
|
||||
robotType: "",
|
||||
ipAddress: "",
|
||||
port: "",
|
||||
currentMapId: ""
|
||||
},
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
},
|
||||
rules: {
|
||||
robotName: [
|
||||
{ required: true, message: "请输入机器人名称", trigger: "change" },
|
||||
],
|
||||
robotCode: [
|
||||
{ required: true, message: "请输入机器人编码", trigger: "change" },
|
||||
],
|
||||
robotType: [
|
||||
{ required: true, message: "请输入机器人类型", trigger: "change" },
|
||||
],
|
||||
ipAddress: [
|
||||
{ required: true, message: "请输入IP地址", trigger: "change" },
|
||||
{
|
||||
pattern: /^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/,
|
||||
message: "请输入正确的IP地址格式(例如:192.168.1.1)",
|
||||
trigger: "change"
|
||||
}
|
||||
],
|
||||
port: [
|
||||
{ required: true, message: "请输入端口号", trigger: "blur" },
|
||||
{
|
||||
pattern: /^\d+$/,
|
||||
message: "端口号只能为数字",
|
||||
trigger: "change"
|
||||
}
|
||||
]
|
||||
},
|
||||
});
|
||||
|
||||
const { queryParams, form, rules } = toRefs(data);
|
||||
|
||||
/** 查询语料库列表 */
|
||||
function getList() {
|
||||
loading.value = true;
|
||||
getRobotList(queryParams.value).then((response) => {
|
||||
tableDataList.value = response.rows;
|
||||
total.value = response.total;
|
||||
loading.value = false;
|
||||
}).catch(() => {
|
||||
loading.value = false;
|
||||
}).finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
const queryParams = reactive({ pageNum: 1, pageSize: 10, robotId: '', robotName: '', connectStatus: '' })
|
||||
const emptyForm = () => ({ id: '', robotId: '', robotName: '', robotCode: '', robotModel: '', robotType: '', currentMapId: '' })
|
||||
const form = reactive(emptyForm())
|
||||
const rules = {
|
||||
robotId: [{ required: true, message: '请输入永久机器人ID', trigger: 'blur' }],
|
||||
robotName: [{ required: true, message: '请输入机器人名称', trigger: 'blur' }],
|
||||
robotCode: [{ required: true, message: '请输入业务编号', trigger: 'blur' }],
|
||||
robotType: [{ required: true, message: '请选择机器人类型', trigger: 'change' }]
|
||||
}
|
||||
|
||||
// 取消按钮
|
||||
function cancel() {
|
||||
open.value = false;
|
||||
reset();
|
||||
const filteredDevices = computed(() => devices.value.filter(item => {
|
||||
const keyword = deviceKeyword.value.trim().toLowerCase()
|
||||
const text = `${item.deviceId || ''} ${item.deviceName || ''} ${item.typeName || ''}`.toLowerCase()
|
||||
return (!keyword || text.includes(keyword)) && (!deviceOnline.value || item.onlineStatus === deviceOnline.value)
|
||||
}))
|
||||
|
||||
async function getList() {
|
||||
loading.value = true
|
||||
try { const res = await getRobotList(queryParams); robotList.value = res.rows || []; total.value = res.total || 0 } finally { loading.value = false }
|
||||
}
|
||||
|
||||
// 表单重置
|
||||
function reset() {
|
||||
robotRef.value.resetFields();
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
function handleQuery() {
|
||||
queryParams.value.pageNum = 1;
|
||||
getList();
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
function resetQuery() {
|
||||
queryParams.value.name = null;
|
||||
queryParams.value.ipAddress = null;
|
||||
handleQuery();
|
||||
}
|
||||
|
||||
|
||||
/** 新增按钮操作 */
|
||||
function handleAdd() {
|
||||
open.value = true;
|
||||
title.value = "添加机器人";
|
||||
isNew.value = true;
|
||||
}
|
||||
|
||||
const connectionStatus = ref(false);
|
||||
/**
|
||||
* 测试连接按钮操作
|
||||
*/
|
||||
const testConnection = () => {
|
||||
// 模拟测试连接的逻辑
|
||||
if (form.value.ipAddress) {
|
||||
if (Math.random() > 0.5) {
|
||||
connectionStatus.value = true;
|
||||
ElMessage.success("连接成功!");
|
||||
} else {
|
||||
connectionStatus.value = false;
|
||||
ElMessage.error("连接失败!");
|
||||
}
|
||||
} else {
|
||||
ElMessage.error("请输入IP地址进行测试连接!");
|
||||
}
|
||||
}
|
||||
|
||||
const ipChange = () => {
|
||||
connectionStatus.value = false; // IP地址改变后重置连接状态
|
||||
}
|
||||
|
||||
/** 修改按钮操作 */
|
||||
function handleUpdate(row) {
|
||||
form.value = { ...row };
|
||||
open.value = true;
|
||||
title.value = "修改机器人";
|
||||
isNew.value = false;
|
||||
}
|
||||
|
||||
/** 提交按钮 */
|
||||
function handleQuery() { queryParams.pageNum = 1; getList() }
|
||||
function resetQuery() { Object.assign(queryParams, { pageNum: 1, robotId: '', robotName: '', connectStatus: '' }); getList() }
|
||||
function handleAdd() { Object.assign(form, emptyForm()); formOpen.value = true }
|
||||
function handleUpdate(row) { Object.assign(form, emptyForm(), row); formOpen.value = true }
|
||||
function submitForm() {
|
||||
robotRef.value.validate((valid) => {
|
||||
if (valid) {
|
||||
if (isNew.value) {
|
||||
// 调用新增接口
|
||||
addRobot(form.value).then(() => {
|
||||
ElMessage.success("机器人添加成功!");
|
||||
getList();
|
||||
reset();
|
||||
open.value = false;
|
||||
});
|
||||
} else {
|
||||
// 调用修改接口
|
||||
updateRobot(form.value).then(() => {
|
||||
ElMessage.success("机器人修改成功!");
|
||||
getList();
|
||||
reset();
|
||||
open.value = false;
|
||||
});
|
||||
}
|
||||
} else {
|
||||
ElMessage.error("请完善表单信息!");
|
||||
return false;
|
||||
}
|
||||
});
|
||||
robotRef.value.validate(async valid => {
|
||||
if (!valid) return
|
||||
submitting.value = true
|
||||
try {
|
||||
if (form.id) await updateRobot(form); else await addRobot(form)
|
||||
ElMessage.success(form.id ? '机器人信息已更新' : '机器人已登记,等待QUIC接入')
|
||||
formOpen.value = false
|
||||
getList()
|
||||
} finally { submitting.value = false }
|
||||
})
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
function handleDelete(row) {
|
||||
ElMessageBox.confirm('确定要删除该机器人吗?', '提示', {}).then(() => {
|
||||
deleteRobot(row.id).then(() => {
|
||||
ElMessage.success("机器人删除成功!");
|
||||
getList();
|
||||
});
|
||||
}).catch(() => {
|
||||
// 用户取消删除
|
||||
});
|
||||
async function handleArchive(row) {
|
||||
await ElMessageBox.confirm(`归档后机器人ID“${row.robotId || row.robotName}”不可再次使用,是否继续?`, '归档机器人', { type: 'warning' })
|
||||
await deleteRobot(row.id); ElMessage.success('机器人已归档'); getList()
|
||||
}
|
||||
|
||||
const getMapArr = () => {
|
||||
getMapList({
|
||||
pageNum: 1,
|
||||
pageSize: 10000,
|
||||
}).then((response) => {
|
||||
mapList.value = response.rows.map(item => ({
|
||||
label: item.mapName,
|
||||
value: item.id
|
||||
}));
|
||||
});
|
||||
async function openDevices(row) {
|
||||
activeRobot.value = row; deviceOpen.value = true; deviceKeyword.value = ''; deviceOnline.value = ''; await loadDevices()
|
||||
clearInterval(refreshTimer); refreshTimer = setInterval(() => { if (deviceOpen.value) loadDevices(false) }, 5000)
|
||||
}
|
||||
async function loadDevices(showLoading = true) {
|
||||
if (!activeRobot.value.id) return
|
||||
if (showLoading) deviceLoading.value = true
|
||||
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 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 stateNames = { 1: '停用', 2: '初始化', 3: '已注册', 4: '就绪', 5: '运行中', 6: '已停止', 7: '错误' }
|
||||
function deviceKindLabel(value) { return kindNames[value] || `未知(${value ?? '-'})` }
|
||||
function managerStateLabel(value) { return stateNames[value] || '-' }
|
||||
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' }
|
||||
|
||||
getList();
|
||||
getMapArr();
|
||||
onBeforeUnmount(() => clearInterval(refreshTimer))
|
||||
getList()
|
||||
getMapList({ pageNum: 1, pageSize: 10000 }).then(res => { mapList.value = (res.rows || []).map(item => ({ label: item.mapName, value: item.id })) })
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.test {
|
||||
position: relative;
|
||||
|
||||
:deep(.el-input__wrapper) {
|
||||
box-shadow: 0 0 0 1px var(--el-input-border-color, var(--el-border-color)) inset;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 1px;
|
||||
background-color: #fff;
|
||||
/* 覆盖右边阴影,需要与背景色相同 */
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-input-group__append) {
|
||||
background-color: transparent;
|
||||
|
||||
.connection {
|
||||
background: #dcdfdf;
|
||||
font-size: 12px;
|
||||
padding: 0 10px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
height: calc(100% - 8px);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.success {
|
||||
background: #67c23a;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<style scoped lang="scss">
|
||||
.robot-page { min-width: 0; }
|
||||
.query-form :deep(.el-input) { width: 190px; }
|
||||
.robot-name, .drawer-title { font-weight: 600; color: var(--el-text-color-primary); }
|
||||
.drawer-title { font-size: 16px; margin-bottom: 4px; }
|
||||
.secondary { color: var(--el-text-color-secondary); font-size: 12px; line-height: 20px; }
|
||||
.identity-text { font-family: ui-monospace, SFMono-Regular, Consolas, monospace; }
|
||||
.fault-tag { margin-left: 6px; }
|
||||
.robot-form { margin-top: 22px; }
|
||||
.device-toolbar { display: grid; grid-template-columns: minmax(220px, 1fr) 150px auto; gap: 10px; margin-bottom: 16px; }
|
||||
@media (max-width: 720px) { .device-toolbar { grid-template-columns: 1fr; } }
|
||||
</style>
|
||||
|
||||
@ -7,8 +7,8 @@
|
||||
:rules="rules"
|
||||
label-width="auto"
|
||||
>
|
||||
<el-form-item label="机器人IP" prop="formData.terminalId" :rules="[{ required: true, message: '请输入', trigger: 'blur' }]">
|
||||
<el-select v-model="formData.terminalId" style="width: 200px">
|
||||
<el-form-item label="执行机器人" prop="formData.robotId" :rules="[{ required: true, message: '请选择在线机器人', trigger: 'change' }]">
|
||||
<el-select v-model="formData.robotId" style="width: 260px" placeholder="请选择在线机器人">
|
||||
<el-option
|
||||
v-for="item in terminalIdOptions"
|
||||
:key="item.value"
|
||||
@ -74,7 +74,7 @@
|
||||
import { ref, watch } from "vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { addValueProperty, formatTableData } from "@/utils/flow";
|
||||
import { listTerminal } from "@/api/device/terminal.js";
|
||||
import { getRobotList } from '@/api/inspection/robot'
|
||||
import { getExecuteRunParams, projectExecute, executeProject } from "@/api/is/project/index.js";
|
||||
|
||||
|
||||
@ -90,7 +90,7 @@ const ruleFormRef = ref();
|
||||
const rules = ref({});
|
||||
|
||||
const formData = ref({
|
||||
terminalId: '',
|
||||
robotId: '',
|
||||
tableData: []
|
||||
})
|
||||
|
||||
@ -98,12 +98,12 @@ const terminalIdOptions = ref([]);
|
||||
|
||||
/** 获取设备终端列表 */
|
||||
const handleGetTerminalGroup = async (row) => {
|
||||
const res = await listTerminal({ pageNum: 1, pageSize: 1000 });
|
||||
const res = await getRobotList({ pageNum: 1, pageSize: 1000, connectStatus: '1' });
|
||||
if (res.code === 200) {
|
||||
terminalIdOptions.value = res.rows?.map((item) => {
|
||||
return {
|
||||
label: `${item.name}(${item.host}:${item.port})`,
|
||||
value: item.id,
|
||||
label: `${item.robotName || item.robotId} (${item.robotId})`,
|
||||
value: item.robotId,
|
||||
};
|
||||
});
|
||||
}
|
||||
@ -138,7 +138,7 @@ const confirm = () => {
|
||||
const data = formatTableData(item.config)
|
||||
runParams[item.itemId] = data
|
||||
})
|
||||
const res = await projectExecute({ runParams, projectId: props.projectId, terminalId: formData.value.terminalId })
|
||||
const res = await projectExecute({ runParams, projectId: props.projectId, robotId: formData.value.robotId })
|
||||
// 后端临时应付检查添加,后续可删除下一行代码,解开上一行代码注释
|
||||
// const res = await executeProject(props.functionIds)
|
||||
if (res.code === 200) {
|
||||
@ -167,4 +167,4 @@ watch(
|
||||
}
|
||||
}
|
||||
);
|
||||
</script>
|
||||
</script>
|
||||
|
||||
@ -7,8 +7,8 @@
|
||||
:rules="rules"
|
||||
label-width="auto"
|
||||
>
|
||||
<el-form-item label="机器人IP" prop="formData.terminalId" :rules="[{ required: true, message: '请输入', trigger: 'blur' }]">
|
||||
<el-select v-model="formData.terminalId" style="width: 200px">
|
||||
<el-form-item label="执行机器人" prop="formData.robotId" :rules="[{ required: true, message: '请选择在线机器人', trigger: 'change' }]">
|
||||
<el-select v-model="formData.robotId" style="width: 260px" placeholder="请选择在线机器人">
|
||||
<el-option
|
||||
v-for="item in terminalIdOptions"
|
||||
:key="item.value"
|
||||
@ -73,7 +73,8 @@ import { ref, watch, onUnmounted } from "vue";
|
||||
import { addValueProperty, formatTableData } from "@/utils/flow";
|
||||
import { emitter } from "@/utils/eventBus";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { listTerminal, getFlowParamsApi } from "@/api/device/terminal.js";
|
||||
import { getFlowParamsApi } from "@/api/device/terminal.js";
|
||||
import { getRobotList } from '@/api/inspection/robot'
|
||||
import { taskExecute } from "@/api/test/config"
|
||||
|
||||
const props = defineProps({
|
||||
@ -87,7 +88,7 @@ const preserveExpanded = ref(false);
|
||||
const ruleFormRef = ref();
|
||||
const rules = ref({});
|
||||
const formData = ref({
|
||||
terminalId: '',
|
||||
robotId: '',
|
||||
tableData: []
|
||||
})
|
||||
|
||||
@ -127,7 +128,7 @@ const confirm = () => {
|
||||
const data = formatTableData(item.config)
|
||||
runParams[item.itemId] = data
|
||||
})
|
||||
const res = await taskExecute({ runParams, taskId: props.currentRow.id, terminalId: formData.value.terminalId })
|
||||
const res = await taskExecute({ runParams, taskId: props.currentRow.id, robotId: formData.value.robotId })
|
||||
if (res.code === 200) {
|
||||
ruleFormRef.value.resetFields();
|
||||
ElMessage.success('执行成功')
|
||||
@ -145,12 +146,12 @@ const terminalIdOptions = ref([]);
|
||||
|
||||
/** 获取设备终端列表 */
|
||||
const handleGetTerminalGroup = async (row) => {
|
||||
const res = await listTerminal({ pageNum: 1, pageSize: 1000 });
|
||||
const res = await getRobotList({ pageNum: 1, pageSize: 1000, connectStatus: '1' });
|
||||
if (res.code === 200) {
|
||||
terminalIdOptions.value = res.rows?.map((item) => {
|
||||
return {
|
||||
label: `${item.name}(${item.host}:${item.port})`,
|
||||
value: item.id,
|
||||
label: `${item.robotName || item.robotId} (${item.robotId})`,
|
||||
value: item.robotId,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
@ -7,8 +7,8 @@
|
||||
:rules="rules"
|
||||
label-width="auto"
|
||||
>
|
||||
<el-form-item label="机器人IP" prop="formData.terminalId" :rules="[{ required: true, message: '请输入', trigger: 'blur' }]">
|
||||
<el-select v-model="formData.terminalId" style="width: 200px">
|
||||
<el-form-item label="执行机器人" prop="formData.robotId" :rules="[{ required: true, message: '请选择在线机器人', trigger: 'change' }]">
|
||||
<el-select v-model="formData.robotId" style="width: 260px" placeholder="请选择在线机器人">
|
||||
<el-option
|
||||
v-for="item in terminalIdOptions"
|
||||
:key="item.value"
|
||||
@ -74,7 +74,7 @@
|
||||
import { ref, watch, onUnmounted } from "vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { addValueProperty, formatTableData } from "@/utils/flow";
|
||||
import { listTerminal } from "@/api/device/terminal.js";
|
||||
import { getRobotList } from '@/api/inspection/robot'
|
||||
import { getExecuteRunParams, projectExecute } from "@/api/vi/project.js"
|
||||
|
||||
const props = defineProps({
|
||||
@ -88,7 +88,7 @@ const ruleFormRef = ref();
|
||||
const rules = ref({});
|
||||
|
||||
const formData = ref({
|
||||
terminalId: '',
|
||||
robotId: '',
|
||||
tableData: []
|
||||
})
|
||||
|
||||
@ -96,12 +96,12 @@ const terminalIdOptions = ref([]);
|
||||
|
||||
/** 获取设备终端列表 */
|
||||
const handleGetTerminalGroup = async (row) => {
|
||||
const res = await listTerminal({ pageNum: 1, pageSize: 1000 });
|
||||
const res = await getRobotList({ pageNum: 1, pageSize: 1000, connectStatus: '1' });
|
||||
if (res.code === 200) {
|
||||
terminalIdOptions.value = res.rows?.map((item) => {
|
||||
return {
|
||||
label: `${item.name}(${item.host}:${item.port})`,
|
||||
value: item.id,
|
||||
label: `${item.robotName || item.robotId} (${item.robotId})`,
|
||||
value: item.robotId,
|
||||
};
|
||||
});
|
||||
}
|
||||
@ -137,7 +137,7 @@ const confirm = () => {
|
||||
const data = formatTableData(item.config)
|
||||
runParams[item.itemId] = data
|
||||
})
|
||||
const res = await projectExecute({ runParams, projectId: props.projectId, terminalId: formData.value.terminalId })
|
||||
const res = await projectExecute({ runParams, projectId: props.projectId, robotId: formData.value.robotId })
|
||||
if (res.code === 200) {
|
||||
ruleFormRef.value.resetFields();
|
||||
ElMessage.success('执行成功')
|
||||
@ -164,4 +164,4 @@ watch(
|
||||
}
|
||||
}
|
||||
);
|
||||
</script>
|
||||
</script>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user