Compare commits

..

No commits in common. "d55f95e7d3c7139708c9d0e6a9c383bf8126007b" and "36c39bb8730626acf54d3c500d44edfdee78d975" have entirely different histories.

7 changed files with 361 additions and 301 deletions

View File

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

View File

@ -30,7 +30,7 @@
</el-table-column> </el-table-column>
<el-table-column prop="value" label="入参值"> <el-table-column prop="value" label="入参值">
<template #default="{ row, column, $index }"> <template #default="{ row, column, $index }">
<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-form-item v-if="row.name === 'terminalId'" label="" :prop="`tableData${row.propPath}value`" :rules="row.required ? [{ required: true, message: '请输入变量名', trigger: 'blur' }] : []">
<el-select v-model="row.value" style="width: 200px"> <el-select v-model="row.value" style="width: 200px">
<el-option <el-option
v-for="item in terminalIdOptions" v-for="item in terminalIdOptions"
@ -66,7 +66,7 @@ import { getStartNodeFormData, formatTableData } from '@/utils/flow'
import { flowExecuteTrial } from '@/api/flow/flow' import { flowExecuteTrial } from '@/api/flow/flow'
import { emitter } from '@/utils/eventBus'; import { emitter } from '@/utils/eventBus';
import { ElMessage } from 'element-plus'; import { ElMessage } from 'element-plus';
import { getRobotList } from '@/api/inspection/robot' import {listTerminal} from "@/api/device/terminal.js";
const props = defineProps({ const props = defineProps({
drawer: Boolean, drawer: Boolean,
@ -104,11 +104,11 @@ const confirm = () => {
}) })
const flowData = JSON.stringify(lf.getGraphData()) const flowData = JSON.stringify(lf.getGraphData())
const { terminalId, robotId, ...runParams } = formData const { terminalId, ...runParams } = formData
const res = await flowExecuteTrial({ const res = await flowExecuteTrial({
flowData, flowData,
itemId: props.flowId, itemId: props.flowId,
robotId: robotId || terminalId, terminalId,
runParams runParams
}) })
if (res.code === 200) { if (res.code === 200) {
@ -127,12 +127,12 @@ const terminalIdOptions = ref([])
/** 获取设备终端列表 */ /** 获取设备终端列表 */
const handleGetTerminalGroup = async (row) => { const handleGetTerminalGroup = async (row) => {
const res = await getRobotList({ pageNum: 1, pageSize: 1000, connectStatus: '1' }) const res = await listTerminal({ pageNum: 1, pageSize: 1000 })
if (res.code === 200) { if (res.code === 200) {
terminalIdOptions.value = res.rows?.map((item) => { terminalIdOptions.value = res.rows?.map((item) => {
return { return {
label: `${item.robotName || item.robotId} (${item.robotId})`, label: `${item.name}(${item.host}:${item.port})`,
value: item.robotId value: item.id
} }
}) })
} }

View File

@ -166,10 +166,10 @@
type="primary" type="primary"
plain plain
:loading="stationLoading" :loading="stationLoading"
:disabled="!singleNodeRobotId || !singleNodeDeviceId" :disabled="!singleNodeTerminalId || !singleNodeDeviceId"
@click="loadSingleNodeStations" @click="loadSingleNodeStations"
>加载站点列表</el-button> >加载站点列表</el-button>
<span>{{ singleNodeRobotId && singleNodeDeviceId ? '加载后从站点下拉框选择' : '请先选择机器人和设备' }}</span> <span>{{ singleNodeTerminalId && singleNodeDeviceId ? '加载后从站点下拉框选择' : '请先填写终端ID和设备ID' }}</span>
</div> </div>
<el-form <el-form
:inline="true" :inline="true"
@ -269,7 +269,6 @@ import {
getAgvStations, getAgvStations,
} from "@/api/flow/flow"; } from "@/api/flow/flow";
import { getDetect } from "@/api/test/detect"; import { getDetect } from "@/api/test/detect";
import { getRobotList } from '@/api/inspection/robot'
import { import {
CircleClose, CircleClose,
CollectionTag, CollectionTag,
@ -891,9 +890,8 @@ const rules = reactive({});
const executeForm = ref(); const executeForm = ref();
const nodeAction = ref(""); const nodeAction = ref("");
const stationLoading = ref(false); const stationLoading = ref(false);
const singleNodeRobotOptions = ref([]); const singleNodeTerminalId = computed(() =>
const singleNodeRobotId = computed(() => formData.nodeParams.find((item) => item.name === "terminalId")?.input
formData.nodeParams.find((item) => item.name === "robotId")?.input
); );
const singleNodeDeviceId = computed(() => const singleNodeDeviceId = computed(() =>
formData.nodeParams.find((item) => item.name === "deviceId")?.input formData.nodeParams.find((item) => item.name === "deviceId")?.input
@ -911,14 +909,14 @@ const loadSingleNodeStations = async () => {
stationLoading.value = true; stationLoading.value = true;
try { try {
const response = await getAgvStations({ const response = await getAgvStations({
robotId: singleNodeRobotId.value, terminalId: singleNodeTerminalId.value,
deviceId: singleNodeDeviceId.value, deviceId: singleNodeDeviceId.value,
}); });
const options = stationOptions(response?.data); const options = stationOptions(response?.data);
const stationParam = formData.nodeParams.find((item) => item.name === "stationId"); const stationParam = formData.nodeParams.find((item) => item.name === "stationId");
if (!stationParam) return; if (!stationParam) return;
stationParam.selectOptions = options; stationParam.selectOptions = options;
stationParam.stationRobotId = singleNodeRobotId.value; stationParam.stationTerminalId = singleNodeTerminalId.value;
stationParam.stationDeviceId = singleNodeDeviceId.value; stationParam.stationDeviceId = singleNodeDeviceId.value;
if (!options.some((item) => item.value === stationParam.input)) stationParam.input = ""; if (!options.some((item) => item.value === stationParam.input)) stationParam.input = "";
if (options.length === 0) ElMessage.warning("当前AGV没有可用站点"); if (options.length === 0) ElMessage.warning("当前AGV没有可用站点");
@ -930,15 +928,7 @@ const loadSingleNodeStations = async () => {
} }
}; };
const loadSingleNodeRobots = async () => { const singleNodeExecution = (e) => {
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 { name, nodeType, nodeParams, action } = e.detail;
const executionParams = JSON.parse(JSON.stringify(nodeParams || [])); const executionParams = JSON.parse(JSON.stringify(nodeParams || []));
nodeName.value = name; nodeName.value = name;
@ -946,15 +936,12 @@ const singleNodeExecution = async (e) => {
nodeAction.value = action; nodeAction.value = action;
stationLoading.value = false; stationLoading.value = false;
if (nodeType === "EDGE") { if (nodeType === "EDGE") {
await loadSingleNodeRobots();
formData.nodeParams = [ formData.nodeParams = [
{ {
disabled: true, disabled: true,
input: "", input: "",
name: "robotId", name: "terminalId",
type: "input", type: "input",
componentType: "select",
selectOptions: singleNodeRobotOptions.value,
}, },
...executionParams, ...executionParams,
]; ];
@ -974,7 +961,7 @@ const execute = async () => {
if (nodeAction.value === "AGV_MOVE_TO_STATION") { if (nodeAction.value === "AGV_MOVE_TO_STATION") {
const stationParam = formData.nodeParams.find((item) => item.name === "stationId"); const stationParam = formData.nodeParams.find((item) => item.name === "stationId");
const validStation = stationParam?.selectOptions?.some((item) => item.value === stationParam.input); const validStation = stationParam?.selectOptions?.some((item) => item.value === stationParam.input);
const correctSource = stationParam?.stationRobotId === singleNodeRobotId.value const correctSource = stationParam?.stationTerminalId === singleNodeTerminalId.value
&& stationParam?.stationDeviceId === singleNodeDeviceId.value; && stationParam?.stationDeviceId === singleNodeDeviceId.value;
if (!validStation || !correctSource) { if (!validStation || !correctSource) {
ElMessage.warning("请按当前终端和设备重新加载并选择站点"); ElMessage.warning("请按当前终端和设备重新加载并选择站点");

View File

@ -1,254 +1,335 @@
<template> <template>
<div class="app-container robot-page"> <div class="app-container">
<el-form v-show="showSearch" :model="queryParams" inline class="query-form"> <div ref="topContainerRef">
<el-form-item label="机器人ID"> <el-form :model="queryParams" :inline="true" v-show="showSearch" label-width="100px">
<el-input v-model="queryParams.robotId" placeholder="永久身份ID" clearable @keyup.enter="handleQuery" /> <el-form-item label="机器人名称" prop="name">
</el-form-item> <el-input v-model="queryParams.name" placeholder="请输入机器人名称" clearable @keyup.enter="handleQuery" />
<el-form-item label="名称"> </el-form-item>
<el-input v-model="queryParams.robotName" placeholder="机器人名称" clearable @keyup.enter="handleQuery" /> <el-form-item label="IP地址" prop="ipAddress">
</el-form-item> <el-input v-model="queryParams.ipAddress" placeholder="请输入IP地址" clearable
<el-form-item label="连接状态"> @keyup.enter="handleQuery" />
<el-select v-model="queryParams.connectStatus" placeholder="全部" clearable style="width: 120px"> </el-form-item>
<el-option label="在线" value="1" /> <el-form-item>
<el-option label="离线" value="0" /> <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
</el-select> <el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item> </el-form-item>
<el-form-item> </el-form>
<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-row :gutter="10" class="mb8">
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd">新增机器人</el-button> <el-button type="primary" plain icon="Plus" @click="handleAdd">新增</el-button>
</el-col> </el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList" /> <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row> </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>
</template>
<div class="device-toolbar"> <div :style="containerHeight">
<el-input v-model="deviceKeyword" placeholder="搜索设备ID或类型" clearable prefix-icon="Search" /> <el-table v-loading="loading" height="100%" style="width: 100%" :data="tableDataList">
<el-select v-model="deviceOnline" placeholder="全部状态" clearable> <el-table-column label="机器人名称" align="center" prop="robotName" />
<el-option label="在线" value="1" /><el-option label="离线" value="0" /> <el-table-column label="机器人编码" align="center" prop="robotCode" />
</el-select> <el-table-column label="机器人类型" align="center" prop="robotType">
<el-button icon="Refresh" :loading="deviceLoading" @click="loadDevices">刷新</el-button> <template #default="scope">
</div> {{ scope.row.robotType == 1 ? '讲解机器人' : '巡检机器人' }}
<el-table v-loading="deviceLoading" :data="filteredDevices" empty-text="机器人尚未上报设备"> </template>
<el-table-column label="设备" min-width="180"> </el-table-column>
<template #default="{ row }"><strong>{{ row.deviceName || row.deviceId }}</strong><div class="secondary">{{ row.deviceId }}</div></template> <el-table-column label="IP地址" align="center" prop="ipAddress" min-width="160" show-overflow-tooltip />
</el-table-column> <el-table-column label="电量" align="center" prop="batteryLevel" />
<el-table-column label="类型" width="120"><template #default="{ row }">{{ deviceKindLabel(row.deviceKind) }}</template></el-table-column> <el-table-column label="状态" align="center" prop="status" />
<el-table-column label="实现" prop="typeName" min-width="150" show-overflow-tooltip /> <el-table-column label="当前地图" align="center" prop="currentMapName" />
<el-table-column label="运行状态" width="110"><template #default="{ row }">{{ managerStateLabel(row.managerState) }}</template></el-table-column> <el-table-column label="操作" class-name="small-padding fixed-width" width="200" fixed="right">
<el-table-column label="健康" width="100"><template #default="{ row }"><el-tag :type="healthTag(row)">{{ healthLabel(row) }}</el-tag></template></el-table-column> <template #default="scope">
<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-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)">修改</el-button>
<el-table-column label="最后发现" prop="lastSeenTime" width="175" /> <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)">删除</el-button>
<el-table-column label="错误" prop="errorMessage" min-width="180" show-overflow-tooltip /> </template>
</el-table> </el-table-column>
</el-drawer> </el-table>
</div>
<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> </template>
<script setup name="InspectionRobot"> <script setup name="Corpus">
import { computed, getCurrentInstance, onBeforeUnmount, reactive, ref } from 'vue' import { ElMessage, ElMessageBox } from "element-plus";
import { ElMessage, ElMessageBox } from 'element-plus' import { useContainerHeight } from "@/hooks/tableHeight";
import { addRobot, deleteRobot, getRobotDevices, getRobotList, updateRobot } from '@/api/inspection/robot' import { addRobot, updateRobot, deleteRobot, getRobotList } from "../../../api/inspection/robot";
import { getMapList } from '@/api/inspection/map' 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);
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 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 queryParams = reactive({ pageNum: 1, pageSize: 10, robotId: '', robotName: '', connectStatus: '' }) const data = reactive({
const emptyForm = () => ({ id: '', robotId: '', robotName: '', robotCode: '', robotModel: '', robotType: '', currentMapId: '' }) form: {
const form = reactive(emptyForm()) robotName: "",
const rules = { robotType: "",
robotId: [{ required: true, message: '请输入永久机器人ID', trigger: 'blur' }], ipAddress: "",
robotName: [{ required: true, message: '请输入机器人名称', trigger: 'blur' }], port: "",
robotCode: [{ required: true, message: '请输入业务编号', trigger: 'blur' }], currentMapId: ""
robotType: [{ required: true, message: '请选择机器人类型', trigger: 'change' }] },
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 filteredDevices = computed(() => devices.value.filter(item => { //
const keyword = deviceKeyword.value.trim().toLowerCase() function cancel() {
const text = `${item.deviceId || ''} ${item.deviceName || ''} ${item.typeName || ''}`.toLowerCase() open.value = false;
return (!keyword || text.includes(keyword)) && (!deviceOnline.value || item.onlineStatus === deviceOnline.value) reset();
}))
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 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 reset() {
function handleUpdate(row) { Object.assign(form, emptyForm(), row); formOpen.value = true } 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 submitForm() { function submitForm() {
robotRef.value.validate(async valid => { robotRef.value.validate((valid) => {
if (!valid) return if (valid) {
submitting.value = true if (isNew.value) {
try { //
if (form.id) await updateRobot(form); else await addRobot(form) addRobot(form.value).then(() => {
ElMessage.success(form.id ? '机器人信息已更新' : '机器人已登记等待QUIC接入') ElMessage.success("机器人添加成功!");
formOpen.value = false getList();
getList() reset();
} finally { submitting.value = false } open.value = false;
}) });
} else {
//
updateRobot(form.value).then(() => {
ElMessage.success("机器人修改成功!");
getList();
reset();
open.value = false;
});
}
} else {
ElMessage.error("请完善表单信息!");
return false;
}
});
} }
async function handleArchive(row) {
await ElMessageBox.confirm(`归档后机器人ID“${row.robotId || row.robotName}”不可再次使用,是否继续?`, '归档机器人', { type: 'warning' })
await deleteRobot(row.id); ElMessage.success('机器人已归档'); getList()
}
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' }
onBeforeUnmount(() => clearInterval(refreshTimer)) /** 删除按钮操作 */
getList() function handleDelete(row) {
getMapList({ pageNum: 1, pageSize: 10000 }).then(res => { mapList.value = (res.rows || []).map(item => ({ label: item.mapName, value: item.id })) }) ElMessageBox.confirm('确定要删除该机器人吗?', '提示', {}).then(() => {
deleteRobot(row.id).then(() => {
ElMessage.success("机器人删除成功!");
getList();
});
}).catch(() => {
//
});
}
const getMapArr = () => {
getMapList({
pageNum: 1,
pageSize: 10000,
}).then((response) => {
mapList.value = response.rows.map(item => ({
label: item.mapName,
value: item.id
}));
});
}
getList();
getMapArr();
</script> </script>
<style lang="scss" scoped>
.test {
position: relative;
<style scoped lang="scss"> :deep(.el-input__wrapper) {
.robot-page { min-width: 0; } box-shadow: 0 0 0 1px var(--el-input-border-color, var(--el-border-color)) inset;
.query-form :deep(.el-input) { width: 190px; }
.robot-name, .drawer-title { font-weight: 600; color: var(--el-text-color-primary); } &::after {
.drawer-title { font-size: 16px; margin-bottom: 4px; } content: '';
.secondary { color: var(--el-text-color-secondary); font-size: 12px; line-height: 20px; } position: absolute;
.identity-text { font-family: ui-monospace, SFMono-Regular, Consolas, monospace; } right: 0;
.fault-tag { margin-left: 6px; } top: 0;
.robot-form { margin-top: 22px; } bottom: 0;
.device-toolbar { display: grid; grid-template-columns: minmax(220px, 1fr) 150px auto; gap: 10px; margin-bottom: 16px; } width: 1px;
@media (max-width: 720px) { .device-toolbar { grid-template-columns: 1fr; } } background-color: #fff;
</style> /* 覆盖右边阴影,需要与背景色相同 */
}
}
: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>

View File

@ -7,8 +7,8 @@
:rules="rules" :rules="rules"
label-width="auto" label-width="auto"
> >
<el-form-item label="执行机器人" prop="formData.robotId" :rules="[{ required: true, message: '请选择在线机器人', trigger: 'change' }]"> <el-form-item label="机器人IP" prop="formData.terminalId" :rules="[{ required: true, message: '请输入', trigger: 'blur' }]">
<el-select v-model="formData.robotId" style="width: 260px" placeholder="请选择在线机器人"> <el-select v-model="formData.terminalId" style="width: 200px">
<el-option <el-option
v-for="item in terminalIdOptions" v-for="item in terminalIdOptions"
:key="item.value" :key="item.value"
@ -74,7 +74,7 @@
import { ref, watch } from "vue"; import { ref, watch } from "vue";
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
import { addValueProperty, formatTableData } from "@/utils/flow"; import { addValueProperty, formatTableData } from "@/utils/flow";
import { getRobotList } from '@/api/inspection/robot' import { listTerminal } from "@/api/device/terminal.js";
import { getExecuteRunParams, projectExecute, executeProject } from "@/api/is/project/index.js"; import { getExecuteRunParams, projectExecute, executeProject } from "@/api/is/project/index.js";
@ -90,7 +90,7 @@ const ruleFormRef = ref();
const rules = ref({}); const rules = ref({});
const formData = ref({ const formData = ref({
robotId: '', terminalId: '',
tableData: [] tableData: []
}) })
@ -98,12 +98,12 @@ const terminalIdOptions = ref([]);
/** 获取设备终端列表 */ /** 获取设备终端列表 */
const handleGetTerminalGroup = async (row) => { const handleGetTerminalGroup = async (row) => {
const res = await getRobotList({ pageNum: 1, pageSize: 1000, connectStatus: '1' }); const res = await listTerminal({ pageNum: 1, pageSize: 1000 });
if (res.code === 200) { if (res.code === 200) {
terminalIdOptions.value = res.rows?.map((item) => { terminalIdOptions.value = res.rows?.map((item) => {
return { return {
label: `${item.robotName || item.robotId} (${item.robotId})`, label: `${item.name}(${item.host}:${item.port})`,
value: item.robotId, value: item.id,
}; };
}); });
} }
@ -138,7 +138,7 @@ const confirm = () => {
const data = formatTableData(item.config) const data = formatTableData(item.config)
runParams[item.itemId] = data runParams[item.itemId] = data
}) })
const res = await projectExecute({ runParams, projectId: props.projectId, robotId: formData.value.robotId }) const res = await projectExecute({ runParams, projectId: props.projectId, terminalId: formData.value.terminalId })
// //
// const res = await executeProject(props.functionIds) // const res = await executeProject(props.functionIds)
if (res.code === 200) { if (res.code === 200) {
@ -167,4 +167,4 @@ watch(
} }
} }
); );
</script> </script>

View File

@ -7,8 +7,8 @@
:rules="rules" :rules="rules"
label-width="auto" label-width="auto"
> >
<el-form-item label="执行机器人" prop="formData.robotId" :rules="[{ required: true, message: '请选择在线机器人', trigger: 'change' }]"> <el-form-item label="机器人IP" prop="formData.terminalId" :rules="[{ required: true, message: '请输入', trigger: 'blur' }]">
<el-select v-model="formData.robotId" style="width: 260px" placeholder="请选择在线机器人"> <el-select v-model="formData.terminalId" style="width: 200px">
<el-option <el-option
v-for="item in terminalIdOptions" v-for="item in terminalIdOptions"
:key="item.value" :key="item.value"
@ -73,8 +73,7 @@ import { ref, watch, onUnmounted } from "vue";
import { addValueProperty, formatTableData } from "@/utils/flow"; import { addValueProperty, formatTableData } from "@/utils/flow";
import { emitter } from "@/utils/eventBus"; import { emitter } from "@/utils/eventBus";
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
import { getFlowParamsApi } from "@/api/device/terminal.js"; import { listTerminal, getFlowParamsApi } from "@/api/device/terminal.js";
import { getRobotList } from '@/api/inspection/robot'
import { taskExecute } from "@/api/test/config" import { taskExecute } from "@/api/test/config"
const props = defineProps({ const props = defineProps({
@ -88,7 +87,7 @@ const preserveExpanded = ref(false);
const ruleFormRef = ref(); const ruleFormRef = ref();
const rules = ref({}); const rules = ref({});
const formData = ref({ const formData = ref({
robotId: '', terminalId: '',
tableData: [] tableData: []
}) })
@ -128,7 +127,7 @@ const confirm = () => {
const data = formatTableData(item.config) const data = formatTableData(item.config)
runParams[item.itemId] = data runParams[item.itemId] = data
}) })
const res = await taskExecute({ runParams, taskId: props.currentRow.id, robotId: formData.value.robotId }) const res = await taskExecute({ runParams, taskId: props.currentRow.id, terminalId: formData.value.terminalId })
if (res.code === 200) { if (res.code === 200) {
ruleFormRef.value.resetFields(); ruleFormRef.value.resetFields();
ElMessage.success('执行成功') ElMessage.success('执行成功')
@ -146,12 +145,12 @@ const terminalIdOptions = ref([]);
/** 获取设备终端列表 */ /** 获取设备终端列表 */
const handleGetTerminalGroup = async (row) => { const handleGetTerminalGroup = async (row) => {
const res = await getRobotList({ pageNum: 1, pageSize: 1000, connectStatus: '1' }); const res = await listTerminal({ pageNum: 1, pageSize: 1000 });
if (res.code === 200) { if (res.code === 200) {
terminalIdOptions.value = res.rows?.map((item) => { terminalIdOptions.value = res.rows?.map((item) => {
return { return {
label: `${item.robotName || item.robotId} (${item.robotId})`, label: `${item.name}(${item.host}:${item.port})`,
value: item.robotId, value: item.id,
}; };
}); });
} }

View File

@ -7,8 +7,8 @@
:rules="rules" :rules="rules"
label-width="auto" label-width="auto"
> >
<el-form-item label="执行机器人" prop="formData.robotId" :rules="[{ required: true, message: '请选择在线机器人', trigger: 'change' }]"> <el-form-item label="机器人IP" prop="formData.terminalId" :rules="[{ required: true, message: '请输入', trigger: 'blur' }]">
<el-select v-model="formData.robotId" style="width: 260px" placeholder="请选择在线机器人"> <el-select v-model="formData.terminalId" style="width: 200px">
<el-option <el-option
v-for="item in terminalIdOptions" v-for="item in terminalIdOptions"
:key="item.value" :key="item.value"
@ -74,7 +74,7 @@
import { ref, watch, onUnmounted } from "vue"; import { ref, watch, onUnmounted } from "vue";
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
import { addValueProperty, formatTableData } from "@/utils/flow"; import { addValueProperty, formatTableData } from "@/utils/flow";
import { getRobotList } from '@/api/inspection/robot' import { listTerminal } from "@/api/device/terminal.js";
import { getExecuteRunParams, projectExecute } from "@/api/vi/project.js" import { getExecuteRunParams, projectExecute } from "@/api/vi/project.js"
const props = defineProps({ const props = defineProps({
@ -88,7 +88,7 @@ const ruleFormRef = ref();
const rules = ref({}); const rules = ref({});
const formData = ref({ const formData = ref({
robotId: '', terminalId: '',
tableData: [] tableData: []
}) })
@ -96,12 +96,12 @@ const terminalIdOptions = ref([]);
/** 获取设备终端列表 */ /** 获取设备终端列表 */
const handleGetTerminalGroup = async (row) => { const handleGetTerminalGroup = async (row) => {
const res = await getRobotList({ pageNum: 1, pageSize: 1000, connectStatus: '1' }); const res = await listTerminal({ pageNum: 1, pageSize: 1000 });
if (res.code === 200) { if (res.code === 200) {
terminalIdOptions.value = res.rows?.map((item) => { terminalIdOptions.value = res.rows?.map((item) => {
return { return {
label: `${item.robotName || item.robotId} (${item.robotId})`, label: `${item.name}(${item.host}:${item.port})`,
value: item.robotId, value: item.id,
}; };
}); });
} }
@ -137,7 +137,7 @@ const confirm = () => {
const data = formatTableData(item.config) const data = formatTableData(item.config)
runParams[item.itemId] = data runParams[item.itemId] = data
}) })
const res = await projectExecute({ runParams, projectId: props.projectId, robotId: formData.value.robotId }) const res = await projectExecute({ runParams, projectId: props.projectId, terminalId: formData.value.terminalId })
if (res.code === 200) { if (res.code === 200) {
ruleFormRef.value.resetFields(); ruleFormRef.value.resetFields();
ElMessage.success('执行成功') ElMessage.success('执行成功')
@ -164,4 +164,4 @@ watch(
} }
} }
); );
</script> </script>