diff --git a/.env.local b/.env.local
index 2d5925a2..835b8acd 100644
--- a/.env.local
+++ b/.env.local
@@ -5,7 +5,7 @@ VITE_DEV=true
# 请求路径
# VITE_BASE_URL='http://192.168.77.50:48080'
-# VITE_BASE_URL='http://10.10.100.19:48080'
+# VITE_BASE_URL='http://10.10.100.15:48080'
VITE_BASE_URL='http://10.10.5.5:48080'
# 文件上传类型:server - 后端上传, client - 前端直连上传,仅支持 S3 服务
diff --git a/src/views/mapPage/realTimeMap/components-tool/BatchCopyingDialogForm.vue b/src/views/mapPage/realTimeMap/components-tool/BatchCopyingDialogForm.vue
new file mode 100644
index 00000000..2b56548f
--- /dev/null
+++ b/src/views/mapPage/realTimeMap/components-tool/BatchCopyingDialogForm.vue
@@ -0,0 +1,125 @@
+
+
+
+
+
+
+
+
diff --git a/src/views/mapPage/realTimeMap/components/indexPage.vue b/src/views/mapPage/realTimeMap/components/indexPage.vue
index 23cf37ea..7fc7a71e 100644
--- a/src/views/mapPage/realTimeMap/components/indexPage.vue
+++ b/src/views/mapPage/realTimeMap/components/indexPage.vue
@@ -883,7 +883,7 @@ onMounted(() => {
// 监听页面可见性变化
document.addEventListener('visibilitychange', () => {
if (!document.hidden && robotListTimerRef.value === null) {
- robotListTimerRef.value = setInterval(getRobotByFloorAndAreaList, 5000)
+ robotListTimerRef.value = setInterval(getRobotByFloorAndAreaList, 10000)
}
})
})
diff --git a/src/views/mapPage/realTimeMap/editMap.vue b/src/views/mapPage/realTimeMap/editMap.vue
index d52247ef..94c637f9 100644
--- a/src/views/mapPage/realTimeMap/editMap.vue
+++ b/src/views/mapPage/realTimeMap/editMap.vue
@@ -284,6 +284,7 @@
toolbarSwitchType === 'createRegion' ||
toolbarSwitchType === 'drawRoute' ||
toolbarSwitchType === 'generateLine' ||
+ toolbarSwitchType === 'batchCopying' ||
toolbarSwitchType === 'bulkDelete'))
"
>
@@ -294,6 +295,7 @@
toolbarSwitchType === 'createRegion' ||
toolbarSwitchType === 'drawRoute' ||
toolbarSwitchType === 'generateLine' ||
+ toolbarSwitchType === 'batchCopying' ||
toolbarSwitchType === 'bulkDelete')
"
type="danger"
@@ -898,6 +900,12 @@
ref="GenerateStraightLinesDialogRef"
@generate-straight-lines-submit="GenerateStraightLinesSubmit"
/>
+
+
@@ -910,6 +918,7 @@ import editNodeProperties from './components-tool/editNodeProperties.vue'
import textFormToolDialog from './components-tool/textFormToolDialog.vue'
import equipmentToolDialog from './components-tool/equipmentToolDialog.vue'
import itemAreaSettingDialog from './components-tool/itemAreaSettingDialog.vue'
+import BatchCopyingDialogForm from './components-tool/BatchCopyingDialogForm.vue'
import lineLibrarySettingDialog from './components-tool/lineLibrarySettingDialog.vue'
import layerSelectionToolDialog from './components-tool/layerSelectionToolDialog.vue'
import itemAreaManagementDialog from './components-tool/itemAreaManagementDialog.vue'
@@ -928,6 +937,7 @@ const lineLibraryManagementDialogRef = ref() //线库管理
const itemAreaManagementDialogRef = ref() //区域管理
const lineLibrarySettingDialogRef = ref() //线库设置
const itemAreaSettingDialogRef = ref() //物料区域设置
+const BatchCopyingDialogFormRef = ref() //批量复制
const equipmentToolDialogRef = ref() //设备弹窗
const textFormToolDialogRef = ref() //文字输入弹窗
const editMapRouteDialogRef = ref() //编辑地图路线的弹窗
@@ -1519,6 +1529,13 @@ const state = reactive({
icon: 'ep:delete',
isActive: false
},
+
+ {
+ switchType: 'batchCopying',
+ name: '批量复制',
+ icon: 'ep:document-copy',
+ isActive: false
+ },
{
switchType: 'showSortNum',
name: '节点序号',
@@ -1566,6 +1583,7 @@ const state = reactive({
drawSelectionAreaBox: { x: 0, y: 0, width: 0, height: 0 }, //绘制区域的位置,长宽
drawSelectionStartPoint: { x: 0, y: 0 }, //开始绘制的点位
drawSelectionPointList: [], //绘制选中的list
+ drawSelectionRouteList: [], //绘制选中的路线list
textFormToolShow: false, //文字表单显示隐藏
showInputBox: false, //输入框显示隐藏
inputBoxStyle: {
@@ -1700,7 +1718,8 @@ const toolbarClick = async (item) => {
toolbarSwitchType.value === 'generateLine' ||
toolbarSwitchType.value === 'createLineLibrary' ||
toolbarSwitchType.value === 'createRegion' ||
- toolbarSwitchType.value === 'bulkDelete'
+ toolbarSwitchType.value === 'bulkDelete' ||
+ toolbarSwitchType.value === 'batchCopying'
) {
state.cursorStyle = 'crosshair'
} else if (
@@ -1734,6 +1753,7 @@ const toolbarClick = async (item) => {
toolbarSwitchType.value === 'createLineLibrary' ||
toolbarSwitchType.value === 'createRegion' ||
toolbarSwitchType.value === 'bulkDelete' ||
+ toolbarSwitchType.value === 'batchCopying' ||
toolbarSwitchType.value === 'editRoute' ||
toolbarSwitchType.value === 'generateLine'
) {
@@ -1936,6 +1956,9 @@ const toolbarClick = async (item) => {
case 'bulkDelete':
// 批量删除
break
+ case 'batchCopying':
+ //批量复制
+ break
case 'showSortNum':
// 显示节点序号
//网格
@@ -2280,7 +2303,8 @@ const startDrawSelection = (event) => {
toolbarSwitchType.value === 'createRegion' ||
toolbarSwitchType.value === 'drawRoute' ||
toolbarSwitchType.value == 'generateLine' ||
- toolbarSwitchType.value === 'bulkDelete'
+ toolbarSwitchType.value === 'bulkDelete' ||
+ toolbarSwitchType.value === 'batchCopying'
) {
const { x, y } = disposeEventPoints(event)
@@ -2301,7 +2325,8 @@ const updateDrawSelection = throttle((event) => {
toolbarSwitchType.value === 'createRegion' ||
toolbarSwitchType.value === 'drawRoute' ||
toolbarSwitchType.value === 'generateLine' ||
- toolbarSwitchType.value === 'bulkDelete'
+ toolbarSwitchType.value === 'bulkDelete' ||
+ toolbarSwitchType.value === 'batchCopying'
) {
if (state.drawSelectionAreaShow) {
const { x, y } = disposeEventPoints(event)
@@ -2405,7 +2430,8 @@ const endDrawSelection = (event) => {
toolbarSwitchType.value === 'createRegion' ||
toolbarSwitchType.value === 'drawRoute' ||
toolbarSwitchType.value === 'generateLine' ||
- toolbarSwitchType.value === 'bulkDelete'
+ toolbarSwitchType.value === 'bulkDelete' ||
+ toolbarSwitchType.value === 'batchCopying'
) {
// 使用 requestAnimationFrame 优化渲染
requestAnimationFrame(() => {
@@ -2421,26 +2447,64 @@ const endDrawSelection = (event) => {
//点击区域
const clickDrawSelectionArea = () => {
- let points = state.allMapPointInfo
-
state.drawSelectionPointList = []
+ state.drawSelectionRouteList = []
+
+ // 用于存储所有框选区域的数据
+ const allSelectedPoints = new Map()
+ const allSelectedRoutes = new Set()
+
state.allDrawSelectionAreaBox.forEach((box) => {
- points.forEach((point) => {
+ // 筛选点
+ state.allMapPointInfo.forEach((point) => {
if (
point.locationX >= box.x &&
point.locationX <= box.x + box.width &&
point.locationY >= box.y &&
point.locationY <= box.y + box.height
) {
- state.drawSelectionPointList.push(point)
+ // 使用位置和类型组合作为唯一标识
+ const pointKey = `${point.locationX},${point.locationY},${point.type}`
+ if (!allSelectedPoints.has(pointKey)) {
+ allSelectedPoints.set(pointKey, point)
+ }
}
})
+
+ // 筛选路线
+ if (toolbarSwitchType.value === 'batchCopying') {
+ state.mapRouteList.forEach((route) => {
+ const startPointInBox =
+ route.startPointX >= box.x &&
+ route.startPointX <= box.x + box.width &&
+ route.startPointY >= box.y &&
+ route.startPointY <= box.y + box.height
+
+ const endPointInBox =
+ route.endPointX >= box.x &&
+ route.endPointX <= box.x + box.width &&
+ route.endPointY >= box.y &&
+ route.endPointY <= box.y + box.height
+
+ if (startPointInBox && endPointInBox) {
+ // 使用起点ID和终点ID组合作为唯一标识
+ const routeKey = `${route.startingPointId}-${route.endPointId}`
+ if (!allSelectedRoutes.has(routeKey)) {
+ allSelectedRoutes.add(routeKey)
+ }
+ }
+ })
+ }
})
+ // 将去重后的数据赋值给状态
+ state.drawSelectionPointList = Array.from(allSelectedPoints.values())
+ state.drawSelectionRouteList = state.mapRouteList.filter((route) =>
+ allSelectedRoutes.has(`${route.startingPointId}-${route.endPointId}`)
+ )
+
// 清空框选区域
state.allDrawSelectionAreaBox = []
- //去重
- state.drawSelectionPointList = deduplicateArrayById(state.drawSelectionPointList)
//只要库位的
let binLocation = state.drawSelectionPointList.filter((item) => item.type === 2)
//所以类型的
@@ -2578,7 +2642,120 @@ const clickDrawSelectionArea = () => {
state.currentItemIndex = -1
addEditHistory()
}
+
+ //批量复制
+ if (toolbarSwitchType.value === 'batchCopying') {
+ if (state.drawSelectionPointList.length > 0) {
+ let boundaryValue = {
+ left: Math.min(...state.drawSelectionPointList.map((point) => Number(point.locationX))),
+ right: Math.max(...state.drawSelectionPointList.map((point) => Number(point.locationX))),
+ top: Math.min(...state.drawSelectionPointList.map((point) => Number(point.locationY))),
+ bottom: Math.max(...state.drawSelectionPointList.map((point) => Number(point.locationY)))
+ }
+ BatchCopyingDialogFormRef.value.open(boundaryValue)
+ } else {
+ message.warning('至少选择一个点')
+ }
+ }
}
+
+//批量复制
+const submitBatchCopyingFormSuccess = async (form) => {
+ let newPoints = JSON.parse(JSON.stringify(state.drawSelectionPointList))
+ let newRoutes = JSON.parse(JSON.stringify(state.drawSelectionRouteList))
+
+ // 创建节点ID映射
+ const nodeIdMap = new Map()
+
+ // 处理节点列表
+ const newPointList = await Promise.all(
+ newPoints.map(async (node) => {
+ const newId = await MapApi.getNodeId()
+ nodeIdMap.set(node.id, newId)
+
+ const locationX = Number(node.locationX) + Number(form.x)
+ const locationY = Number(node.locationY) + Number(form.y)
+ const actualPoint = disposeEventPoint(locationX, locationY)
+
+ const { sortNum, createTime, ...restNode } = node
+ return {
+ ...restNode,
+ id: newId,
+ locationX,
+ locationY,
+ actualLocationX: actualPoint.actualLocationX,
+ actualLocationY: actualPoint.actualLocationY
+ }
+ })
+ )
+
+ // 处理路线列表
+ const newRouteList = newRoutes.map((route) => {
+ const newRoute = { ...route }
+
+ // 更新起点
+ if (nodeIdMap.has(route.startingPointId)) {
+ newRoute.startingPointId = nodeIdMap.get(route.startingPointId)
+ newRoute.startPointX = (Number(route.startPointX) + Number(form.x)).toString()
+ newRoute.startPointY = (Number(route.startPointY) + Number(form.y)).toString()
+
+ const actualStartPoint = disposeEventPoint(newRoute.startPointX, newRoute.startPointY)
+ newRoute.actualStartPointX = actualStartPoint.actualLocationX
+ newRoute.actualStartPointY = actualStartPoint.actualLocationY
+
+ // 更新控制点
+ if (route.method == 1) {
+ newRoute.beginControlX = (Number(route.beginControlX) + Number(form.x)).toString()
+ newRoute.beginControlY = (Number(route.beginControlY) + Number(form.y)).toString()
+ const actualBeginControl = disposeEventPoint(newRoute.beginControlX, newRoute.beginControlY)
+ newRoute.actualBeginControlX = actualBeginControl.actualLocationX
+ newRoute.actualBeginControlY = actualBeginControl.actualLocationY
+ } else {
+ newRoute.beginControlX = 0
+ newRoute.beginControlY = 0
+ newRoute.actualBeginControlX = 0
+ newRoute.actualBeginControlY = 0
+ }
+ }
+
+ // 更新终点
+ if (nodeIdMap.has(route.endPointId)) {
+ newRoute.endPointId = nodeIdMap.get(route.endPointId)
+ newRoute.endPointX = (Number(route.endPointX) + Number(form.x)).toString()
+ newRoute.endPointY = (Number(route.endPointY) + Number(form.y)).toString()
+
+ const actualEndPoint = disposeEventPoint(newRoute.endPointX, newRoute.endPointY)
+ newRoute.actualEndPointX = actualEndPoint.actualLocationX
+ newRoute.actualEndPointY = actualEndPoint.actualLocationY
+
+ // 更新控制点
+ if (route.method == 1) {
+ newRoute.endControlX = (Number(route.endControlX) + Number(form.x)).toString()
+ newRoute.endControlY = (Number(route.endControlY) + Number(form.y)).toString()
+ const actualEndControl = disposeEventPoint(newRoute.endControlX, newRoute.endControlY)
+ newRoute.actualEndControlX = actualEndControl.actualLocationX
+ newRoute.actualEndControlY = actualEndControl.actualLocationY
+ } else {
+ newRoute.endControlX = 0
+ newRoute.endControlY = 0
+ newRoute.actualEndControlX = 0
+ newRoute.actualEndControlY = 0
+ }
+ }
+
+ const { id, createTime, startingSortNum, endPointSortNum, ...restRoute } = newRoute
+ return restRoute
+ })
+
+ // 添加到地图
+ console.log(newPointList, newRouteList)
+
+ state.allMapPointInfo.push(...newPointList)
+ state.mapRouteList.push(...newRouteList)
+ message.success('复制成功')
+ addEditHistory()
+}
+
//生成直线 选择完成开始点和结束点
const GenerateStraightLinesSubmit = (pointList, form) => {
// 使用 requestAnimationFrame 优化渲染
@@ -2630,24 +2807,13 @@ const GenerateStraightLinesSubmit = (pointList, form) => {
}
return item
})
-
// 批量更新状态
state.allMapPointInfo = updatedPoints
state.mapRouteList = updatedRoutes
addEditHistory()
})
}
-//去重
-const deduplicateArrayById = (arr) => {
- const idSet = new Set()
- return arr.filter((item) => {
- if (idSet.has(item.id)) {
- return false
- }
- idSet.add(item.id)
- return true
- })
-}
+
//返回生成线库时 排序相同项的id
const findDuplicateLocationIds = (data) => {
const locationMap = {}
@@ -3341,6 +3507,7 @@ const saveMap = async () => {
message.success('保存成功')
} catch (error) {
loading.close()
+ message.error(error?.message || '保存失败')
}
}
//节点的保存
@@ -3374,11 +3541,19 @@ const saveNodeList = async () => {
item.dataJson = JSON.stringify(item.dataObj)
}
})
- await MapApi.batchSaveOrEditOrDelMapItem(imgBgObj.positionMapId, list)
+ try {
+ await MapApi.batchSaveOrEditOrDelMapItem(imgBgObj.positionMapId, list)
+ } catch (error) {
+ throw new Error('节点保存失败:' + (error?.message || '未知错误'))
+ }
}
//路线的保存
const saveMapRoute = async () => {
- await MapApi.createOrEditOrDelPositionMapLine(imgBgObj.positionMapId, state.mapRouteList)
+ try {
+ await MapApi.createOrEditOrDelPositionMapLine(imgBgObj.positionMapId, state.mapRouteList)
+ } catch (error) {
+ throw new Error('路线保存失败:' + (error?.message || '未知错误'))
+ }
}
//线库新增 要在库位新增线库信息
const submitLineLibraryFormSuccess = (obj) => {
@@ -3556,15 +3731,7 @@ const getLineMidArrowPath = (item) => {
return `M ${startXArrow} ${startYArrow} L ${endXArrow} ${endYArrow}`
}
-// 计算贝塞尔曲线中间箭头的路径(简单近似)
-const getBezierMidArrowPath = (item) => {
- const path = document.createElementNS('http://www.w3.org/2000/svg', 'path')
- path.setAttribute('d', item.curvePath)
- const length = path.getTotalLength()
- const midPoint = path.getPointAtLength(length / 2)
- const prevPoint = path.getPointAtLength(length / 2 - 1)
- return `M ${prevPoint.x} ${prevPoint.y} L ${midPoint.x} ${midPoint.y}`
-}
+
// 计算贝塞尔曲线中间文字的 x 坐标
const computedCurveTextX = (item) => {
return (
@@ -3851,6 +4018,8 @@ const findClosestPoint = (x, y) => {
padding: 0 0.75rem;
.top-tool-list {
+ max-width: calc(100vw - 260px);
+ overflow-x: auto;
display: flex;
align-items: center;
text-align: center;
@@ -3896,11 +4065,13 @@ const findClosestPoint = (x, y) => {
.right-tool-list {
position: absolute;
right: 0;
- top: 64px;
+ top: 70px;
background-color: #fff;
z-index: 999;
text-align: center;
box-shadow: rgba(0, 0, 0, 0.05) 0rem 0rem 0rem 0.0625rem;
+ max-height: calc(100vh - 200px);
+ overflow-y: auto;
.tool-item {
cursor: pointer;
@@ -4008,4 +4179,33 @@ const findClosestPoint = (x, y) => {
height: 20px;
}
}
+
+// 美化滚动条
+::-webkit-scrollbar {
+ width: 10px;
+ height: 10px;
+}
+
+::-webkit-scrollbar-track {
+ width: 6px;
+ background: rgba(#101f1c, 0.1);
+ -webkit-border-radius: 2em;
+ -moz-border-radius: 2em;
+ border-radius: 2em;
+}
+
+::-webkit-scrollbar-thumb {
+ background-color: rgba(144, 147, 153, 0.7);
+ background-clip: padding-box;
+ min-height: 28px;
+ -webkit-border-radius: 2em;
+ -moz-border-radius: 2em;
+ border-radius: 2em;
+ transition: background-color 0.3s;
+ cursor: pointer;
+}
+
+::-webkit-scrollbar-thumb:hover {
+ background-color: rgba(144, 147, 153, 0.3);
+}