feat: 点位管理和机器人

This commit is contained in:
zhanghao 2026-06-01 11:04:18 +08:00
parent 4e8998216f
commit d198189d2c
9 changed files with 857 additions and 309 deletions

33
src/api/inspection/map.js Normal file
View File

@ -0,0 +1,33 @@
import request from '@/utils/request'
export function getMapList(params) {
return request({
url: '/inspection/map/list',
method: 'get',
params: params
})
}
export function addMap(data) {
return request({
url: '/inspection/map',
method: 'post',
data: data
})
}
export function updateMap(data) {
return request({
url: '/inspection/map',
method: 'put',
data: data
})
}
export function deleteMap(id) {
return request({
url: `/inspection/map/${id}`,
method: 'delete'
})
}

View File

@ -0,0 +1,32 @@
import request from '@/utils/request'
export function getRobotList(params) {
return request({
url: '/inspection/robot/list',
method: 'get',
params: params
})
}
export function addRobot(data) {
return request({
url: '/inspection/robot',
method: 'post',
data: data
})
}
export function updateRobot(data) {
return request({
url: '/inspection/robot',
method: 'put',
data: data
})
}
export function deleteRobot(id) {
return request({
url: `/inspection/robot/${id}`,
method: 'delete'
})
}

View File

@ -88,7 +88,7 @@ app.use(ElementPlus, {
app.use(WebSocketPlugin, { app.use(WebSocketPlugin, {
// url: import.meta.env.VITE_WS_URL, // url: import.meta.env.VITE_WS_URL,
url: 'ws://192.168.0.100:13080/ws', url: 'ws://192.168.0.114:13080/ws',
// url: 'ws://10.148.209.5:13080/ws', // url: 'ws://10.148.209.5:13080/ws',
userId: 'your_user_id' userId: 'your_user_id'
}); });

View File

@ -9,7 +9,7 @@ export const useInspectionStore = defineStore('inspection', {
camera: { // 相机参数 camera: { // 相机参数
centerX: 0, // 相机中心的世界坐标 X centerX: 0, // 相机中心的世界坐标 X
centerY: 0, // 相机中心的世界坐标 Y centerY: 0, // 相机中心的世界坐标 Y
pixelsPerMeter: 40, // 缩放:每米对应的屏幕像素数 pixelsPerMeter: 20, // 缩放:每米对应的屏幕像素数
}, },
canvasWidthPx: 0, // Canvas CSS 逻辑宽度(像素) canvasWidthPx: 0, // Canvas CSS 逻辑宽度(像素)
canvasHeightPx: 0, // Canvas CSS 逻辑高度(像素) canvasHeightPx: 0, // Canvas CSS 逻辑高度(像素)

View File

@ -153,7 +153,7 @@ async function loadMapIntoApplication(parsedMap) {
const worldHeightMeters = parsedMap.maxY - parsedMap.originY; // const worldHeightMeters = parsedMap.maxY - parsedMap.originY; //
const fitScaleX = inspectionStore.canvasWidthPx / worldWidthMeters; // const fitScaleX = inspectionStore.canvasWidthPx / worldWidthMeters; //
const fitScaleY = inspectionStore.canvasHeightPx / worldHeightMeters; // const fitScaleY = inspectionStore.canvasHeightPx / worldHeightMeters; //
inspectionStore.camera.pixelsPerMeter = Math.max(1, Math.min(fitScaleX, fitScaleY) * 0.88); // 12% inspectionStore.camera.pixelsPerMeter = Math.max(1, Math.min(fitScaleX, fitScaleY) * 1); //
// UI // UI
applicationState.isMapLoaded = true; applicationState.isMapLoaded = true;

View File

@ -1,3 +1,307 @@
<template> <template>
<div>流程管理</div> <div class="app-container">
</template> <div ref="topContainerRef">
<TableSearch
:queryParams="queryParams"
:showSearch="showSearch"
label-width="90px"
queryRef="queryRef"
@refresh="resetQuery"
@search="handleQuery"
>
<template #one>
<el-col :lg="6" :md="12" :sm="24" :xl="6" :xs="24" :xxl="6">
<el-form-item label="点位名称" prop="waypointName">
<el-input
v-model="queryParams.waypointName"
clearable
placeholder="请输入点位名称"
style="width: 100%"
@keyup.enter="handleQuery"
/>
</el-form-item>
</el-col>
<el-col :lg="6" :md="12" :sm="24" :xl="6" :xs="24" :xxl="6">
<el-form-item label="点位描述" prop="remark">
<el-input
v-model="queryParams.remark"
clearable
placeholder="请输入点位描述"
style="width: 100%"
@keyup.enter="handleQuery"
/>
</el-form-item>
</el-col>
</template>
</TableSearch>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
v-hasPermi="['test:detect:add']"
icon="Plus"
plain
type="primary"
@click="handleAdd"
>新增
</el-button>
</el-col>
<right-toolbar
v-model:showSearch="showSearch"
@queryTable="getList"
></right-toolbar>
</el-row>
</div>
<div :style="containerHeight">
<el-table
v-loading="loading"
height="100%"
:data="tableDataList"
>
<el-table-column
align="center"
label="id"
prop="id"
show-overflow-tooltip
/>
<el-table-column
align="center"
label="点位名称"
prop="waypointName"
show-overflow-tooltip
/>
<el-table-column
align="center"
label="点位描述"
prop="remark"
show-overflow-tooltip
/>
<el-table-column align="center" label="启用状态" prop="enabled">
<template #default="scope">
<el-switch
v-model="scope.row.enabled"
active-text="启用"
active-value="0"
inactive-text="禁用"
inactive-value="1"
inline-prompt
@change="handleChange(scope.row)"
/>
</template>
</el-table-column>
<el-table-column align="center" label="配置状态" prop="isDeploy">
<template #default="scope">
<TableTags
:options="[
{
label: '已配置',
value: '0',
type: 'success',
},
{
label: '未配置',
value: '1',
type: 'warning',
},
]"
:value="scope.row.isDeploy"
></TableTags>
</template>
</el-table-column>
<el-table-column
align="center"
label="创建人"
prop="createBy"
show-overflow-tooltip
/>
<el-table-column
align="center"
label="创建时间"
prop="createTime"
show-overflow-tooltip
/>
<el-table-column
align="center"
class-name="small-padding fixed-width"
label="操作"
width="200"
>
<template #default="scope">
<el-button
icon="Edit"
link
type="primary"
@click="handleUpdate(scope.row)"
>
修改
</el-button>
<el-button
icon="Cpu"
link
type="primary"
@click="handleDeploy(scope.row)"
>配置
</el-button>
<el-button
icon="Delete"
link
type="primary"
@click="handleDelete(scope.row)"
>删除
</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total > 0"
v-model:limit="queryParams.pageSize"
v-model:page="queryParams.pageNum"
:total="total"
@pagination="getList"
/>
</div>
<!-- 添加或修改检测项配置对话框 -->
<el-dialog v-model="open" :title="title" append-to-body width="500px">
<el-form ref="detectRef" :model="form" :rules="rules" label-width="100px">
<el-form-item label="点位名称" prop="waypointName">
<el-input v-model="form.waypointName" placeholder="请输入点位名称" />
</el-form-item>
<el-form-item label="点位描述" prop="remark">
<el-input v-model="form.remark" placeholder="请输入点位描述" />
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input
v-model="form.remark"
:rows="2"
placeholder="请输入备注"
type="textarea"
/>
</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>
<el-dialog v-model="dialogVisible" title="参数配置" width="1080px">
<EditTable></EditTable>
</el-dialog>
</div>
</template>
<script name="Detect" setup>
import {
addDetect,
delDetect,
getDetect,
listDetect,
updateDetect,
} from "@/api/test/detect";
import TableSearch from "@/components/TableSearch/index.vue";
import { listGroup } from "@/api/system/group.js";
import { appendParamsToPath } from "@/utils/fn.js";
import TableTags from "@/components/TableTags/index.vue";
import EditTable from "@/components/EditTable/index.vue";
import { useContainerHeight } from "@/hooks/tableHeight";
const topContainerRef = ref();
const containerHeight = useContainerHeight(topContainerRef);
const { proxy } = getCurrentInstance();
const tableDataList = ref([]);
const open = ref(false);
const dialogVisible = ref(false);
const loading = ref(false);
const showSearch = ref(true);
const total = ref(0);
const title = ref("");
const data = reactive({
form: {},
queryParams: {
pageNum: 1,
pageSize: 10,
waypointName: null,
remark: null,
},
rules: {
waypointName: [
{ required: true, message: "点位名称不能为空", trigger: "blur" },
],
},
});
const { queryParams, form, rules } = toRefs(data);
/** 查询检测项配置列表 */
function getList() {
// loading.value = true;
}
//
function cancel() {
open.value = false;
reset();
}
//
function reset() {
}
/** 搜索按钮操作 */
function handleQuery() {
queryParams.value.pageNum = 1;
getList();
}
/** 重置按钮操作 */
function resetQuery() {
handleQuery();
}
/** 新增按钮操作 */
function handleAdd() {
reset();
open.value = true;
title.value = "添加检测项配置";
}
/** 修改按钮操作 */
function handleUpdate(row) {
}
/** 提交按钮 */
function submitForm() {
}
/** 删除按钮操作 */
function handleDelete(row) {
}
/** 配置按钮操作 */
function handleDeploy(row) {
const params = { itemId: row.id, name: row.waypointName, state: row.status };
const fullPath = appendParamsToPath("/flow", params);
const p = window.open(fullPath, "_blank");
}
onMounted(() => {
getList();
});
</script>

View File

@ -1,12 +1,12 @@
<template> <template>
<div class="app-container"> <div class="app-container">
<div ref="topContainerRef"> <div ref="topContainerRef">
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px"> <el-form :model="queryParams" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="地图名称" prop="corpusName"> <el-form-item label="地图名称" prop="mapName">
<el-input v-model="queryParams.corpusName" placeholder="请输入地图名称" clearable @keyup.enter="handleQuery" /> <el-input v-model="queryParams.mapName" placeholder="请输入地图名称" clearable @keyup.enter="handleQuery" />
</el-form-item> </el-form-item>
<el-form-item label="地图描述" prop="voiceType"> <el-form-item label="地图描述" prop="mapDesc">
<el-input v-model="queryParams.voiceType" placeholder="请输入地图描述" clearable @keyup.enter="handleQuery" /> <el-input v-model="queryParams.mapDesc" placeholder="请输入地图描述" clearable @keyup.enter="handleQuery" />
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button> <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
@ -24,10 +24,10 @@
<div :style="containerHeight"> <div :style="containerHeight">
<el-table v-loading="loading" height="100%" style="width: 100%" :data="tableDataList"> <el-table v-loading="loading" height="100%" style="width: 100%" :data="tableDataList">
<el-table-column label="地图名称" align="center" prop="corpusName" /> <el-table-column label="地图名称" align="center" prop="mapName" />
<el-table-column label="地图描述" align="center" prop="textContent" min-width="160" show-overflow-tooltip /> <el-table-column label="地图描述" align="center" prop="mapDesc" min-width="160" show-overflow-tooltip />
<el-table-column label="创建人" align="center" prop="status" /> <el-table-column label="创建人" align="center" prop="createBy" />
<el-table-column label="创建时间" align="center" prop="status" /> <el-table-column label="创建时间" align="center" prop="createTime" />
<el-table-column label="操作" class-name="small-padding fixed-width" width="200" fixed="right"> <el-table-column label="操作" class-name="small-padding fixed-width" width="200" fixed="right">
<template #default="scope"> <template #default="scope">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)">修改</el-button> <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)">修改</el-button>
@ -42,19 +42,19 @@
<!-- 添加或修改语料库对话框 --> <!-- 添加或修改语料库对话框 -->
<el-dialog :title="title" v-model="open" width="580px" append-to-body> <el-dialog :title="title" v-model="open" width="580px" append-to-body>
<el-form ref="corpusRef" :model="form" :rules="rules" label-width="120px"> <el-form ref="mapRef" :model="form" :rules="rules" label-width="120px">
<el-form-item label="地图名称" prop="corpusName"> <el-form-item label="地图名称" prop="mapName">
<el-input v-model="form.corpusName" placeholder="请输入地图名称" /> <el-input v-model="form.mapName" placeholder="请输入地图名称" />
</el-form-item> </el-form-item>
<el-form-item label="地图描述" prop="textContent"> <el-form-item label="地图描述" prop="mapDesc">
<el-input v-model="form.textContent" type="textarea" placeholder="请输入地图描述" /> <el-input v-model="form.mapDesc" type="textarea" placeholder="请输入地图描述" />
</el-form-item> </el-form-item>
<el-form-item label="选择机器人地图" prop="dialect"> <el-form-item label="选择机器人地图">
<el-col :span="11"> <el-col :span="11">
<el-form-item prop="date1"> <el-form-item prop="mapSourceRobotId">
<el-select v-model="form.robotId" placeholder="请选择机器人" clearable style="width: 100%"> <el-select v-model="form.mapSourceRobotId" placeholder="请选择机器人" clearable style="width: 100%"
<el-option label="机器人1" value="zh"></el-option> @change="robotChange">
<el-option label="机器人2" value="en"></el-option> <el-option v-for="robot in robotList" :key="robot.value" :label="robot.label" :value="robot.value" />
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
@ -62,10 +62,10 @@
<span class="text-gray-500">-</span> <span class="text-gray-500">-</span>
</el-col> </el-col>
<el-col :span="11"> <el-col :span="11">
<el-form-item prop="date2"> <el-form-item prop="mapSourceName">
<el-select v-model="form.mapId" placeholder="请选择地图" clearable style="width: 100%" @change="mapChange"> <el-select v-model="form.mapSourceName" placeholder="请选择地图" clearable style="width: 100%"
<el-option label="地图1" value="zh"></el-option> @change="mapChange">
<el-option label="地图2" value="en"></el-option> <el-option v-for="map in mapList" :key="map.value" :label="map.label" :value="map.value" />
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
@ -84,30 +84,35 @@
</div> </div>
</template> </template>
<script setup name="Corpus"> <script setup name="Map">
import { useContainerHeight } from "@/hooks/tableHeight"; import { useContainerHeight } from "@/hooks/tableHeight";
import { addMap, updateMap, deleteMap, getMapList } from "../../../api/inspection/map";
import { getRobotList } from "../../../api/inspection/robot";
import { onMounted } from "vue"; import { onMounted } from "vue";
import { import {
worldToScreen, worldToScreen,
screenToWorld, screenToWorld,
drawGrid, drawGrid,
drawGridlines, drawGridlines,
drawAdvancedAreaList, drawAdvancedAreaList,
drawAdvancedLineList, drawAdvancedLineList,
drawNormalLines, drawNormalLines,
drawAdvancedCurveList, drawAdvancedCurveList,
drawAdvancedPointList, drawAdvancedPointList,
drawMapBoundary, drawMapBoundary,
buildOccupancyGridImage, buildOccupancyGridImage,
MobileRobot MobileRobot
} from '../cockpit/canvasUtils'; } from '../cockpit/canvasUtils';
import { de } from "element-plus/es/locale/index.mjs";
import { ElMessage, ElMessageBox } from "element-plus";
const topContainerRef = ref(); const topContainerRef = ref();
const containerHeight = useContainerHeight(topContainerRef); const containerHeight = useContainerHeight(topContainerRef);
const mapRef = ref();
const isNew = ref(true);
const tableDataList = ref([]); const tableDataList = ref([]);
const open = ref(false); const open = ref(false);
@ -119,21 +124,24 @@ const title = ref("");
const data = reactive({ const data = reactive({
form: { form: {
type: "WAKEUP", mapName: "",
mapDesc: "",
mapSourceRobotId: "",
mapSourceName: "",
}, },
queryParams: { queryParams: {
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 10,
mapDesc: null,
mapName: null,
}, },
rules: { rules: {
corpusName: [ mapName: [
{ required: true, message: "请输入语料名称", trigger: "change" }, { required: true, message: "请输入地图名称", trigger: "change" },
], ],
textContent: [ mapDesc: [
{ required: true, message: "请输入语音文本", trigger: "blur" }, { required: true, message: "请输入地图描述", trigger: "blur" },
], ]
voiceType: [{ required: true, message: "请选择音色", trigger: "blur" }],
dialect: [{ required: true, message: "请选择语种", trigger: "blur" }],
}, },
}); });
@ -142,11 +150,15 @@ const { queryParams, form, rules } = toRefs(data);
/** 查询列表 */ /** 查询列表 */
function getList() { function getList() {
loading.value = true; loading.value = true;
getMapList(queryParams.value).then((response) => {
tableDataList.value = []; tableDataList.value = response.rows;
total.value = 0; total.value = response.total;
loading.value = false; loading.value = false;
}).catch(() => {
loading.value = false;
}).finally(() => {
loading.value = false;
});
} }
// //
@ -154,8 +166,8 @@ function cancel() {
open.value = false; open.value = false;
reset(); reset();
if (animationId) { if (animationId) {
cancelAnimationFrame(animationId); cancelAnimationFrame(animationId);
animationId = null; animationId = null;
} }
window.removeEventListener('resize', resizeCanvasToContainer) window.removeEventListener('resize', resizeCanvasToContainer)
@ -164,9 +176,13 @@ function cancel() {
// //
function reset() { function reset() {
mapRef.value.resetFields();
form.value = { form.value = {
}; mapName: "",
mapDesc: "",
mapSourceRobotId: "",
mapSourceName: "",
}
} }
/** 搜索按钮操作 */ /** 搜索按钮操作 */
@ -177,114 +193,159 @@ function handleQuery() {
/** 重置按钮操作 */ /** 重置按钮操作 */
function resetQuery() { function resetQuery() {
queryParams.value.mapName = null;
queryParams.value.mapDesc = null;
handleQuery(); handleQuery();
} }
/** 新增按钮操作 */ /** 新增按钮操作 */
function handleAdd() { function handleAdd() {
reset();
open.value = true; open.value = true;
isNew.value = true;
title.value = "添加地图"; title.value = "添加地图";
setTimeout(() => { setTimeout(() => {
init(); init();
}, 300); }, 300);
} }
/** 修改按钮操作 */ /** 修改按钮操作 */
function handleUpdate(row) { function handleUpdate(row) {
form.value = { ...row };
isNew.value = false;
open.value = true;
title.value = "修改地图";
setTimeout(() => {
init();
if (form.value.mapSourceName) {
mapChange()
}
}, 300);
} }
/** 提交按钮 */ /** 提交按钮 */
function submitForm() { function submitForm() {
mapRef.value.validate((valid) => {
if (valid) {
if (isNew.value) {
//
addMap(form.value).then(() => {
ElMessage.success("地图添加成功!");
getList();
reset();
open.value = false;
});
ElMessage.success("地图添加成功!");
getList();
} else {
//
updateMap(form.value).then(() => {
ElMessage.success("地图修改成功!");
getList();
reset();
open.value = false;
});
}
}
});
} }
/** 删除按钮操作 */ /** 删除按钮操作 */
function handleDelete(row) { function handleDelete(row) {
ElMessageBox.confirm('确定要删除该地图吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}).then(() => {
deleteMap(row.id).then(() => {
ElMessage.success("地图删除成功!");
getList();
});
}).catch(() => {
//
});
} }
const loadSourceMap = async (url) => { const loadSourceMap = async (url) => {
const response = await fetch(url); const response = await fetch(url);
if (!response.ok) throw new Error(`加载失败:${response.status}`); if (!response.ok) throw new Error(`加载失败:${response.status}`);
return response.text(); return response.text();
}; };
function parseSmapJson(rawJson) { function parseSmapJson(rawJson) {
// header // header
const header = rawJson.header || rawJson; // header const header = rawJson.header || rawJson; // header
const minimumPos = header.minPos || { x: 0, y: 0 }; // const minimumPos = header.minPos || { x: 0, y: 0 }; //
const maximumPos = header.maxPos || { x: 1, y: 1 }; // const maximumPos = header.maxPos || { x: 1, y: 1 }; //
const resolution = header.resolution || 0.05; // const resolution = header.resolution || 0.05; //
// //
const worldWidth = maximumPos.x - minimumPos.x; // const worldWidth = maximumPos.x - minimumPos.x; //
const worldHeight = maximumPos.y - minimumPos.y; // const worldHeight = maximumPos.y - minimumPos.y; //
const gridWidthPx = Math.ceil(worldWidth / resolution) + 1; // const gridWidthPx = Math.ceil(worldWidth / resolution) + 1; //
const gridHeightPx = Math.ceil(worldHeight / resolution) + 1;// const gridHeightPx = Math.ceil(worldHeight / resolution) + 1;//
// //
const parsedMap = { const parsedMap = {
name: header.mapName || 'Untitled', // name: header.mapName || 'Untitled', //
version: header.version || '', // version: header.version || '', //
mapType: header.mapType || '', // "2D-Map" mapType: header.mapType || '', // "2D-Map"
resolution: resolution, // / resolution: resolution, // /
originX: minimumPos.x, // X originX: minimumPos.x, // X
originY: minimumPos.y, // Y originY: minimumPos.y, // Y
maxX: maximumPos.x, // X maxX: maximumPos.x, // X
maxY: maximumPos.y, // Y maxY: maximumPos.y, // Y
gridWidth: gridWidthPx, // gridWidth: gridWidthPx, //
gridHeight: gridHeightPx, // gridHeight: gridHeightPx, //
// normalPosList: // normalPosList:
normalPositions: rawJson.normalPosList || [], normalPositions: rawJson.normalPosList || [],
// normalLineList: {startPos:{x,y}, endPos:{x,y}} // normalLineList: {startPos:{x,y}, endPos:{x,y}}
normalLines: rawJson.normalLineList || [], normalLines: rawJson.normalLineList || [],
// advancedPointList: {className, instanceName, pos:{x,y}, dir, desc, property:[]} // advancedPointList: {className, instanceName, pos:{x,y}, dir, desc, property:[]}
advancedPoints: rawJson.advancedPointList || [], advancedPoints: rawJson.advancedPointList || [],
// advancedLineList: {className, instanceName, line:{startPos, endPos}} // advancedLineList: {className, instanceName, line:{startPos, endPos}}
advancedLines: rawJson.advancedLineList || [], advancedLines: rawJson.advancedLineList || [],
// advancedCurveList: {className, instanceName, controlPos1, controlPos2, startPos, endPos} // advancedCurveList: {className, instanceName, controlPos1, controlPos2, startPos, endPos}
advancedCurves: rawJson.advancedCurveList || [], advancedCurves: rawJson.advancedCurveList || [],
// advancedAreaList: {className, instanceName, dir, posGroup:[], attribute:{}} // advancedAreaList: {className, instanceName, dir, posGroup:[], attribute:{}}
advancedAreas: rawJson.advancedAreaList || [], advancedAreas: rawJson.advancedAreaList || [],
}; };
return parsedMap; return parsedMap;
} }
const applicationState = { const applicationState = {
parsedMap: null, // parsedMap: null, //
gridOffscreen: null, // Canvas gridOffscreen: null, // Canvas
hoveredElement: null, // {kind, index} hoveredElement: null, // {kind, index}
animationTimeSeconds: 0, // animationTimeSeconds: 0, //
camera: { // camera: { //
centerX: 0, // X centerX: 0, // X
centerY: 0, // Y centerY: 0, // Y
pixelsPerMeter: 40, // pixelsPerMeter: 20, //
}, },
canvasWidthPx: 0, // Canvas CSS canvasWidthPx: 0, // Canvas CSS
canvasHeightPx: 0, // Canvas CSS canvasHeightPx: 0, // Canvas CSS
devicePixelRatio: 1, // DPI > 1 devicePixelRatio: 1, // DPI > 1
mouseState: { // mouseState: { //
screenX: 0, // Canvas X screenX: 0, // Canvas X
screenY: 0, // Canvas Y screenY: 0, // Canvas Y
worldX: 0, // X worldX: 0, // X
worldY: 0, // Y worldY: 0, // Y
isDragging: false, // isDragging: false, //
}, },
isMapLoaded: false, // , isMapLoaded: false, // ,
layerVisibility: { // layerVisibility: { //
grid: true, // grid: true, //
edges: true, // edges: true, //
gridlines: true, // 1线 gridlines: true, // 1线
normalLines: true, // normalLineList normalLines: true, // normalLineList
advLines: true, // advancedLineList advLines: true, // advancedLineList
curves: true, // advancedCurveList curves: true, // advancedCurveList
points: true, // advancedPointList points: true, // advancedPointList
areas: true, // advancedAreaList areas: true, // advancedAreaList
} }
}; };
let mapCanvas; // DOM let mapCanvas; // DOM
@ -300,179 +361,214 @@ const init = () => {
* 在窗口 resize 时自动调用 * 在窗口 resize 时自动调用
*/ */
function resizeCanvasToContainer() { function resizeCanvasToContainer() {
const containerRect = mapCanvas.parentElement.getBoundingClientRect(); // const containerRect = mapCanvas.parentElement.getBoundingClientRect(); //
console.log('Container size:', containerRect.width, 'x', containerRect.height); console.log('containerRect', containerRect)
devicePixelRatio = window.devicePixelRatio || 1; // devicePixelRatio = window.devicePixelRatio || 1; //
applicationState.canvasWidthPx = containerRect.width; // applicationState.canvasWidthPx = containerRect.width; //
applicationState.canvasHeightPx = containerRect.height; // = - applicationState.canvasHeightPx = containerRect.height; // = -
mapCanvas.width = applicationState.canvasWidthPx * devicePixelRatio; // Canvas mapCanvas.width = applicationState.canvasWidthPx * devicePixelRatio; // Canvas
mapCanvas.height = applicationState.canvasHeightPx * devicePixelRatio; // Canvas mapCanvas.height = applicationState.canvasHeightPx * devicePixelRatio; // Canvas
mapCanvas.style.width = applicationState.canvasWidthPx + 'px'; // CSS mapCanvas.style.width = applicationState.canvasWidthPx + 'px'; // CSS
mapCanvas.style.height = applicationState.canvasHeightPx + 'px'; // CSS mapCanvas.style.height = applicationState.canvasHeightPx + 'px'; // CSS
} }
async function loadMapIntoApplication(parsedMap) { async function loadMapIntoApplication(parsedMap) {
await new Promise(resolve => setTimeout(resolve, 30)); // UI await new Promise(resolve => setTimeout(resolve, 30)); // UI
// //
applicationState.parsedMap = parsedMap; applicationState.parsedMap = parsedMap;
// //
applicationState.gridOffscreen = buildOccupancyGridImage(parsedMap, applicationState.layerVisibility.edges); applicationState.gridOffscreen = buildOccupancyGridImage(parsedMap, applicationState.layerVisibility.edges);
// //
applicationState.camera.centerX = (parsedMap.originX + parsedMap.maxX) / 2; // applicationState.camera.centerX = (parsedMap.originX + parsedMap.maxX) / 2; //
applicationState.camera.centerY = (parsedMap.originY + parsedMap.maxY) / 2; // applicationState.camera.centerY = (parsedMap.originY + parsedMap.maxY) / 2; //
// 使 // 使
const worldWidthMeters = parsedMap.maxX - parsedMap.originX; // const worldWidthMeters = parsedMap.maxX - parsedMap.originX; //
const worldHeightMeters = parsedMap.maxY - parsedMap.originY; // const worldHeightMeters = parsedMap.maxY - parsedMap.originY; //
const fitScaleX = applicationState.canvasWidthPx / worldWidthMeters; // const fitScaleX = applicationState.canvasWidthPx / worldWidthMeters; //
const fitScaleY = applicationState.canvasHeightPx / worldHeightMeters; // const fitScaleY = applicationState.canvasHeightPx / worldHeightMeters; //
applicationState.camera.pixelsPerMeter = Math.max(1, Math.min(fitScaleX, fitScaleY) * 0.88); // 12% applicationState.camera.pixelsPerMeter = Math.max(1, Math.min(fitScaleX, fitScaleY)); // 12%
// UI // UI
applicationState.isMapLoaded = true; applicationState.isMapLoaded = true;
} }
const bindEvent = () => { const bindEvent = () => {
// //
mapCanvas.addEventListener('mousemove', (mouseEvent) => { mapCanvas.addEventListener('mousemove', (mouseEvent) => {
const canvasRect = mapCanvas.getBoundingClientRect(); // Canvas const canvasRect = mapCanvas.getBoundingClientRect(); // Canvas
applicationState.mouseState.screenX = mouseEvent.clientX - canvasRect.left; // Canvas X applicationState.mouseState.screenX = mouseEvent.clientX - canvasRect.left; // Canvas X
applicationState.mouseState.screenY = mouseEvent.clientY - canvasRect.top; // Canvas Y applicationState.mouseState.screenY = mouseEvent.clientY - canvasRect.top; // Canvas Y
// //
const worldPos = screenToWorld(applicationState.mouseState.screenX, applicationState.mouseState.screenY, applicationState); const worldPos = screenToWorld(applicationState.mouseState.screenX, applicationState.mouseState.screenY, applicationState);
applicationState.mouseState.worldX = worldPos.x; applicationState.mouseState.worldX = worldPos.x;
applicationState.mouseState.worldY = worldPos.y; applicationState.mouseState.worldY = worldPos.y;
// //
if (applicationState.mouseState.isDragging) { if (applicationState.mouseState.isDragging) {
applicationState.camera.centerX -= mouseEvent.movementX / applicationState.camera.pixelsPerMeter; // applicationState.camera.centerX -= mouseEvent.movementX / applicationState.camera.pixelsPerMeter; //
applicationState.camera.centerY += mouseEvent.movementY / applicationState.camera.pixelsPerMeter; // Y applicationState.camera.centerY += mouseEvent.movementY / applicationState.camera.pixelsPerMeter; // Y
} }
}); });
// //
mapCanvas.addEventListener('mousedown', () => { mapCanvas.addEventListener('mousedown', () => {
applicationState.mouseState.isDragging = true; // applicationState.mouseState.isDragging = true; //
}); });
// //
mapCanvas.addEventListener('mouseup', () => { mapCanvas.addEventListener('mouseup', () => {
applicationState.mouseState.isDragging = false; // applicationState.mouseState.isDragging = false; //
}); });
// Tooltip // Tooltip
mapCanvas.addEventListener('mouseleave', () => { mapCanvas.addEventListener('mouseleave', () => {
applicationState.mouseState.isDragging = false; applicationState.mouseState.isDragging = false;
}); });
// //
mapCanvas.addEventListener('wheel', (wheelEvent) => { mapCanvas.addEventListener('wheel', (wheelEvent) => {
wheelEvent.preventDefault(); // wheelEvent.preventDefault(); //
const zoomFactor = wheelEvent.deltaY > 0 ? 0.88 : 1.12; // == const zoomFactor = wheelEvent.deltaY > 0 ? 0.88 : 1.12; // ==
const mouseWorldBefore = screenToWorld(applicationState.mouseState.screenX, applicationState.mouseState.screenY, applicationState); // const mouseWorldBefore = screenToWorld(applicationState.mouseState.screenX, applicationState.mouseState.screenY, applicationState); //
applicationState.camera.pixelsPerMeter = Math.max(0.5, Math.min(2000, applicationState.camera.pixelsPerMeter * zoomFactor)); // applicationState.camera.pixelsPerMeter = Math.max(0.5, Math.min(2000, applicationState.camera.pixelsPerMeter * zoomFactor)); //
const mouseWorldAfter = screenToWorld(applicationState.mouseState.screenX, applicationState.mouseState.screenY, applicationState); // const mouseWorldAfter = screenToWorld(applicationState.mouseState.screenX, applicationState.mouseState.screenY, applicationState); //
// 使 // 使
applicationState.camera.centerX += mouseWorldBefore.x - mouseWorldAfter.x; applicationState.camera.centerX += mouseWorldBefore.x - mouseWorldAfter.x;
applicationState.camera.centerY += mouseWorldBefore.y - mouseWorldAfter.y; applicationState.camera.centerY += mouseWorldBefore.y - mouseWorldAfter.y;
}, { passive: false }); // passive:false preventDefault }, { passive: false }); // passive:false preventDefault
} }
/** /**
* 加载 smap JSON 文件 * 加载 smap JSON 文件
*/ */
const loadUserFile = async () => { const loadUserFile = async () => {
try { try {
const fileTextContent = await loadSourceMap('/3.smap'); const filemapDesc = await loadSourceMap('/3.smap');
const rawJsonObject = JSON.parse(fileTextContent); // JSON const rawJsonObject = JSON.parse(filemapDesc); // JSON
const parsedMap = parseSmapJson(rawJsonObject); // const parsedMap = parseSmapJson(rawJsonObject); //
await loadMapIntoApplication(parsedMap); // await loadMapIntoApplication(parsedMap); //
} catch (parseError) { } catch (parseError) {
alert('Parse error: ' + parseError.message); // alert('Parse error: ' + parseError.message); //
} }
} }
// requestAnimationFrame ID // requestAnimationFrame ID
let animationId = null; let animationId = null;
function renderFrame(timestamp) { function renderFrame(timestamp) {
console.log("渲染中") //
// applicationState.animationTimeSeconds = (timestamp || 0) / 1000;
applicationState.animationTimeSeconds = (timestamp || 0) / 1000;
// Canvas // Canvas
canvasCtx.setTransform(applicationState.devicePixelRatio, 0, 0, applicationState.devicePixelRatio, 0, 0); canvasCtx.setTransform(applicationState.devicePixelRatio, 0, 0, applicationState.devicePixelRatio, 0, 0);
canvasCtx.clearRect(0, 0, applicationState.canvasWidthPx, applicationState.canvasHeightPx); //
//
if (!applicationState.isMapLoaded) {
animationId = requestAnimationFrame(renderFrame); //
return;
}
const map = applicationState.parsedMap; //
const layers = applicationState.layerVisibility; //
// 1:
if (layers.grid && applicationState.gridOffscreen) {
drawGrid(canvasCtx, map, applicationState);
}
// 2: 线1
if (layers.gridlines) {
drawGridlines(canvasCtx, applicationState)
}
// 3: advancedAreaList
if (layers.areas) {
drawAdvancedAreaList(canvasCtx, map, applicationState)
}
// 4: normalLineList线 #00e5ff
if (layers.normalLines) {
drawNormalLines(canvasCtx, map, applicationState);
}
// 5: advancedLineList线 ForbiddenLine
if (layers.advLines) {
drawAdvancedLineList(canvasCtx, map, applicationState);
}
// 6: advancedCurveList线绿 #76ff03
if (layers.curves) {
drawAdvancedCurveList(canvasCtx, map, applicationState);
}
// 7: advancedPointList
if (layers.points) {
drawAdvancedPointList(canvasCtx, map, applicationState);
}
// 线
drawMapBoundary(canvasCtx, map, applicationState);
//
animationId = requestAnimationFrame(renderFrame);
}
const robotList = ref([])
const mapList = ref([])
const getRobotArr = () => {
getRobotList({
pageNum: 1,
pageSize: 10000,
}).then((response) => {
robotList.value = response.rows.map(item => ({
label: item.robotName,
value: item.id
}));
});
}
const getMapArr = (robotId) => {
// TODO: robotId
mapList.value = [
{ label: "地图1", value: "map1" },
{ label: "地图2", value: "map2" },
{ label: "地图3", value: "map3" },
]
}
const robotChange = (value) => {
form.value.mapSourceRobotId = value; // ID
form.value.mapSourceName = ""; //
if (canvasCtx) {
canvasCtx.clearRect(0, 0, applicationState.canvasWidthPx, applicationState.canvasHeightPx); // canvasCtx.clearRect(0, 0, applicationState.canvasWidthPx, applicationState.canvasHeightPx); //
}
// getMapArr(value);
if (!applicationState.isMapLoaded) {
animationId = requestAnimationFrame(renderFrame); //
return;
}
const map = applicationState.parsedMap; //
const layers = applicationState.layerVisibility; //
// 1:
if (layers.grid && applicationState.gridOffscreen) {
drawGrid(canvasCtx, map, applicationState);
}
// 2: 线1
if (layers.gridlines) {
drawGridlines(canvasCtx, applicationState)
}
// 3: advancedAreaList
if (layers.areas) {
drawAdvancedAreaList(canvasCtx, map, applicationState)
}
// 4: normalLineList线 #00e5ff
if (layers.normalLines) {
drawNormalLines(canvasCtx, map, applicationState);
}
// 5: advancedLineList线 ForbiddenLine
if (layers.advLines) {
drawAdvancedLineList(canvasCtx, map, applicationState);
}
// 6: advancedCurveList线绿 #76ff03
if (layers.curves) {
drawAdvancedCurveList(canvasCtx, map, applicationState);
}
// 7: advancedPointList
if (layers.points) {
drawAdvancedPointList(canvasCtx, map, applicationState);
}
// 线
drawMapBoundary(canvasCtx, map, applicationState);
//
animationId = requestAnimationFrame(renderFrame);
} }
const mapChange = () => { const mapChange = () => {
loadUserFile(); loadUserFile();
window.addEventListener('resize', resizeCanvasToContainer); //
resizeCanvasToContainer();
if (animationId) {
cancelAnimationFrame(animationId);
}
animationId = requestAnimationFrame(renderFrame);
setTimeout(() => { if (animationId) {
bindEvent() cancelAnimationFrame(animationId);
}, 100)
} }
animationId = requestAnimationFrame(renderFrame);
window.addEventListener('resize', resizeCanvasToContainer); //
resizeCanvasToContainer();
setTimeout(() => {
bindEvent()
}, 100)
}
onMounted(() => { onMounted(() => {
getList(); getList();
getRobotArr();
}); });
</script> </script>

View File

@ -1,10 +1,9 @@
<template> <template>
<div class="app-container"> <div class="app-container">
<div ref="topContainerRef"> <div ref="topContainerRef">
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="100px"> <el-form :model="queryParams" :inline="true" v-show="showSearch" label-width="100px">
<el-form-item label="机器人名称" prop="name"> <el-form-item label="机器人名称" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入机器人名称" clearable <el-input v-model="queryParams.name" placeholder="请输入机器人名称" clearable @keyup.enter="handleQuery" />
@keyup.enter="handleQuery" />
</el-form-item> </el-form-item>
<el-form-item label="IP地址" prop="ipAddress"> <el-form-item label="IP地址" prop="ipAddress">
<el-input v-model="queryParams.ipAddress" placeholder="请输入IP地址" clearable <el-input v-model="queryParams.ipAddress" placeholder="请输入IP地址" clearable
@ -26,11 +25,12 @@
<div :style="containerHeight"> <div :style="containerHeight">
<el-table v-loading="loading" height="100%" style="width: 100%" :data="tableDataList"> <el-table v-loading="loading" height="100%" style="width: 100%" :data="tableDataList">
<el-table-column label="机器人名称" align="center" prop="name" /> <el-table-column label="机器人名称" align="center" prop="robotName" />
<el-table-column label="机器人类型" align="center" prop="robotType" />
<el-table-column label="IP地址" align="center" prop="ipAddress" min-width="160" show-overflow-tooltip /> <el-table-column label="IP地址" align="center" prop="ipAddress" min-width="160" show-overflow-tooltip />
<el-table-column label="电量" align="center" prop="battery" /> <el-table-column label="电量" align="center" prop="batteryLevel" />
<el-table-column label="状态" align="center" prop="status" /> <el-table-column label="状态" align="center" prop="status" />
<el-table-column label="当前地图" align="center" prop="currentMap" /> <el-table-column label="当前地图" align="center" prop="currentMapName" />
<el-table-column label="操作" class-name="small-padding fixed-width" width="200" fixed="right"> <el-table-column label="操作" class-name="small-padding fixed-width" width="200" fixed="right">
<template #default="scope"> <template #default="scope">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)">修改</el-button> <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)">修改</el-button>
@ -45,9 +45,15 @@
<!-- 添加或修改语料库对话框 --> <!-- 添加或修改语料库对话框 -->
<el-dialog :title="title" v-model="open" width="580px" append-to-body> <el-dialog :title="title" v-model="open" width="580px" append-to-body>
<el-form :model="form" :rules="rules" label-width="100px"> <el-form ref="robotRef" :model="form" :rules="rules" label-width="100px">
<el-form-item label="机器人名称" prop="name"> <el-form-item label="机器人名称" prop="robotName">
<el-input v-model="form.name" placeholder="请输入机器人名称" /> <el-input v-model="form.robotName" 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>
<el-form-item label="IP地址" prop="ipAddress"> <el-form-item label="IP地址" prop="ipAddress">
<el-input class="test" v-model="form.ipAddress" placeholder="请输入IP地址" @input="ipChange"> <el-input class="test" v-model="form.ipAddress" placeholder="请输入IP地址" @input="ipChange">
@ -58,9 +64,11 @@
</template> </template>
</el-input> </el-input>
</el-form-item> </el-form-item>
<el-form-item label="端口号" prop="port">
<el-form-item label="地图选择" prop="currentMap"> <el-input v-model="form.port" placeholder="请输入端口号" />
<el-select v-model="form.currentMap" placeholder="请选择地图" clearable style="width: 100%"> </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-option v-for="map in mapList" :key="map.value" :label="map.label" :value="map.value" />
</el-select> </el-select>
</el-form-item> </el-form-item>
@ -76,8 +84,17 @@
</template> </template>
<script setup name="Corpus"> <script setup name="Corpus">
import { ElMessage } from "element-plus"; import { ElMessage, ElMessageBox } from "element-plus";
import { useContainerHeight } from "@/hooks/tableHeight"; 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 topContainerRef = ref();
const containerHeight = useContainerHeight(topContainerRef); const containerHeight = useContainerHeight(topContainerRef);
@ -89,29 +106,44 @@ const showSearch = ref(true);
const total = ref(0); const total = ref(0);
const title = ref(""); const title = ref("");
const isNew = ref(true);
const mapList = ref([ const mapList = ref([])
{ label: "地图1", value: "map1" },
{ label: "地图2", value: "map2" },
{ label: "地图3", value: "map3" },
])
const data = reactive({ const data = reactive({
form: { form: {
name: "", robotName: "",
robotType: "",
ipAddress: "", ipAddress: "",
currentMap: "" port: "",
currentMapId: ""
}, },
queryParams: { queryParams: {
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 10,
}, },
rules: { rules: {
name: [ robotName: [
{ required: true, message: "请输入机器人名称", trigger: "change" }, { required: true, message: "请输入机器人名称", trigger: "change" },
], ],
robotType: [
{ required: true, message: "请输入机器人类型", trigger: "change" },
],
ipAddress: [ ipAddress: [
{ required: true, message: "请输入IP地址", trigger: "blur" }, { 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"
}
] ]
}, },
}); });
@ -121,10 +153,15 @@ const { queryParams, form, rules } = toRefs(data);
/** 查询语料库列表 */ /** 查询语料库列表 */
function getList() { function getList() {
loading.value = true; loading.value = true;
tableDataList.value = []; getRobotList(queryParams.value).then((response) => {
total.value = 0; tableDataList.value = response.rows;
loading.value = false; total.value = response.total;
loading.value = false;
}).catch(() => {
loading.value = false;
}).finally(() => {
loading.value = false;
});
} }
// //
@ -135,12 +172,7 @@ function cancel() {
// //
function reset() { function reset() {
form.value = { robotRef.value.resetFields();
name: "",
ipAddress: "",
currentMap: ""
};
} }
/** 搜索按钮操作 */ /** 搜索按钮操作 */
@ -151,18 +183,23 @@ function handleQuery() {
/** 重置按钮操作 */ /** 重置按钮操作 */
function resetQuery() { function resetQuery() {
queryParams.value.name = null;
queryParams.value.ipAddress = null;
handleQuery(); handleQuery();
} }
/** 新增按钮操作 */ /** 新增按钮操作 */
function handleAdd() { function handleAdd() {
reset();
open.value = true; open.value = true;
title.value = "添加机器人"; title.value = "添加机器人";
isNew.value = true;
} }
const connectionStatus = ref(false); const connectionStatus = ref(false);
/**
* 测试连接按钮操作
*/
const testConnection = () => { const testConnection = () => {
// //
if (form.value.ipAddress) { if (form.value.ipAddress) {
@ -184,21 +221,66 @@ const ipChange = () => {
/** 修改按钮操作 */ /** 修改按钮操作 */
function handleUpdate(row) { function handleUpdate(row) {
form.value = { ...row };
open.value = true;
title.value = "修改机器人";
isNew.value = false;
} }
/** 提交按钮 */ /** 提交按钮 */
function submitForm() { 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;
}
});
} }
/** 删除按钮操作 */ /** 删除按钮操作 */
function handleDelete(row) { function handleDelete(row) {
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(); getList();
getMapArr();
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.test { .test {
@ -206,7 +288,7 @@ getList();
:deep(.el-input__wrapper) { :deep(.el-input__wrapper) {
box-shadow: 0 0 0 1px var(--el-input-border-color, var(--el-border-color)) inset; box-shadow: 0 0 0 1px var(--el-input-border-color, var(--el-border-color)) inset;
&::after { &::after {
content: ''; content: '';
position: absolute; position: absolute;
@ -214,7 +296,8 @@ getList();
top: 0; top: 0;
bottom: 0; bottom: 0;
width: 1px; width: 1px;
background-color: #fff; /* 覆盖右边阴影,需要与背景色相同 */ background-color: #fff;
/* 覆盖右边阴影,需要与背景色相同 */
} }
} }

View File

@ -47,11 +47,11 @@ export default defineConfig(({mode, command}) => {
proxy: { proxy: {
// https://cn.vitejs.dev/config/#server-proxy // https://cn.vitejs.dev/config/#server-proxy
'/dev-api': { '/dev-api': {
//服务器 http://192.168.0.100:13080 //服务器 http://192.168.0.114:13080
//李小龙 http://192.168.0.5:13080 //李小龙 http://192.168.0.5:13080
// dev http://10.148.20.34:13080 // dev http://10.148.20.34:13080
// target: VITE_API_URL, // target: VITE_API_URL,
target: command === 'build' ? VITE_API_URL : 'http://192.168.28.10:13080', target: command === 'build' ? VITE_API_URL : 'http://192.168.0.114:13080',
changeOrigin: true, changeOrigin: true,
rewrite: (p) => p.replace(/^\/dev-api/, '') rewrite: (p) => p.replace(/^\/dev-api/, '')
} }