批量复制
This commit is contained in:
parent
dd713321b1
commit
2e148ea659
@ -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 服务
|
||||
|
@ -0,0 +1,125 @@
|
||||
<template>
|
||||
<!-- 批量复制 -->
|
||||
<Dialog
|
||||
v-model="dialogFormVisible"
|
||||
title="批量复制"
|
||||
width="500"
|
||||
class="batch-copying-dialog-form"
|
||||
@close="dialogClose"
|
||||
>
|
||||
<el-form :model="batchCopyingForm" label-width="100" ref="BatchCopyingFormRef" :rules="rules">
|
||||
<el-form-item label="X轴偏移量" prop="x" required>
|
||||
<el-input-number
|
||||
v-model="batchCopyingForm.x"
|
||||
placeholder="请输入"
|
||||
controls-position="right"
|
||||
style="width: 100%"
|
||||
precision="2"
|
||||
/>
|
||||
<el-text type="info" size="small">X轴往左为负值,往右为正值</el-text>
|
||||
</el-form-item>
|
||||
<el-form-item label="Y轴偏移量" prop="y" required>
|
||||
<el-input-number
|
||||
v-model="batchCopyingForm.y"
|
||||
placeholder="请输入"
|
||||
controls-position="right"
|
||||
style="width: 100%"
|
||||
precision="2"
|
||||
/>
|
||||
<el-text type="info" size="small">X轴往上为负值,往下为正值</el-text>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="dialogFormVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="submitLineLibraryForm()"> 确定 </el-button>
|
||||
</div>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, ref } from 'vue'
|
||||
import * as MapApi from '@/api/map/map'
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const props = defineProps({
|
||||
imgBgObj: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
}
|
||||
})
|
||||
//新增
|
||||
const batchCopyingForm = ref({
|
||||
x: 0,
|
||||
y: 0
|
||||
})
|
||||
const boundaryValue = ref()
|
||||
const BatchCopyingFormRef = ref()
|
||||
const dialogFormVisible = ref(false) //列表的
|
||||
|
||||
const validateXValue = (rule, value, callback) => {
|
||||
let maxLeft = -Number(boundaryValue.value.left)
|
||||
let maxRight = Number(props.imgBgObj.width) - Number(boundaryValue.value.right)
|
||||
if (value < maxLeft || value > maxRight) {
|
||||
callback(new Error(`不能超出地图宽度,可偏移范围为${maxLeft}至${maxRight}`))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
const validateYValue = (rule, value, callback) => {
|
||||
let maxTop = -Number(boundaryValue.value.top)
|
||||
let maxBottom = Number(props.imgBgObj.height) - Number(boundaryValue.value.bottom)
|
||||
if (value < maxTop || value > maxBottom) {
|
||||
callback(new Error(`不能超出地图宽度,可偏移范围为${maxTop}至${maxBottom}`))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
|
||||
const rules = reactive({
|
||||
x: [
|
||||
{ required: true, message: '请输入X轴偏移量', trigger: 'blur' },
|
||||
{ validator: validateXValue, trigger: 'blur' }
|
||||
],
|
||||
y: [
|
||||
{ required: true, message: '请输入Y轴偏移量', trigger: 'blur' },
|
||||
{ validator: validateYValue, trigger: 'blur' }
|
||||
]
|
||||
})
|
||||
|
||||
const open = (boundaryObj) => {
|
||||
boundaryValue.value = boundaryObj
|
||||
batchCopyingForm.value.x = 0
|
||||
batchCopyingForm.value.y = 0
|
||||
dialogFormVisible.value = true
|
||||
}
|
||||
|
||||
const emit = defineEmits(['addEventListener', 'submitBatchCopyingFormSuccess'])
|
||||
const dialogClose = () => {
|
||||
emit('addEventListener')
|
||||
}
|
||||
|
||||
const submitLineLibraryForm = async () => {
|
||||
await BatchCopyingFormRef.value.validate(async (valid, fields) => {
|
||||
if (valid) {
|
||||
dialogFormVisible.value = false
|
||||
emit('submitBatchCopyingFormSuccess', batchCopyingForm.value)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.batch-copying-dialog-form {
|
||||
padding: 0px;
|
||||
|
||||
.el-dialog__footer {
|
||||
padding: 0px 20px 20px 0;
|
||||
border-top: none !important;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -883,7 +883,7 @@ onMounted(() => {
|
||||
// 监听页面可见性变化
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
if (!document.hidden && robotListTimerRef.value === null) {
|
||||
robotListTimerRef.value = setInterval(getRobotByFloorAndAreaList, 5000)
|
||||
robotListTimerRef.value = setInterval(getRobotByFloorAndAreaList, 10000)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
@ -284,6 +284,7 @@
|
||||
toolbarSwitchType === 'createRegion' ||
|
||||
toolbarSwitchType === 'drawRoute' ||
|
||||
toolbarSwitchType === 'generateLine' ||
|
||||
toolbarSwitchType === 'batchCopying' ||
|
||||
toolbarSwitchType === 'bulkDelete'))
|
||||
"
|
||||
></div>
|
||||
@ -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"
|
||||
/>
|
||||
<!-- 批量复制节点和路线 -->
|
||||
<BatchCopyingDialogForm
|
||||
ref="BatchCopyingDialogFormRef"
|
||||
:imgBgObj="imgBgObj"
|
||||
@submit-batch-copying-form-success="submitBatchCopyingFormSuccess"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -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);
|
||||
}
|
||||
</style>
|
||||
|
Loading…
Reference in New Issue
Block a user