Compare commits

..

2 Commits

Author SHA1 Message Date
d55f95e7d3 Merge remote-tracking branch 'origin/lxl-dev' into lxl-dev 2026-08-04 17:29:32 +08:00
210e4cf716 feat(flow): 将流程节点中的终端ID替换为机器人ID
- 替换singleNodeTerminalId为singleNodeRobotId
- 更新流程图中机器人选择功能,使用机器人ID替代终端ID
- 修改AGV移动到站点功能以使用机器人ID
- 更新任务执行页面中的机器人选择组件
- 重构机器人管理页面,添加设备管理功能
- 实现机器人详情抽屉和设备列表展示
- 添加机器人归档功能和在线状态显示
2026-08-04 17:29:07 +08:00
7 changed files with 299 additions and 359 deletions

View File

@ -31,6 +31,13 @@ export function deleteRobot(id) {
})
}
export function getRobotDevices(id) {
return request({
url: `/inspection/robot/${id}/devices`,
method: 'get'
})
}
/**
* 获取机器人地图
* @param {*} robotId

View File

@ -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/flow/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
}
})
}

View File

@ -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/flow/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("请按当前终端和设备重新加载并选择站点");

View File

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

View File

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

View File

@ -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,
};
});
}

View File

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